Add RISC-V support. 78/4578/5
authorTim Newsome <tim@sifive.com>
Wed, 18 Jul 2018 20:34:23 +0000 (13:34 -0700)
committerMatthias Welwarsky <matthias@welwarsky.de>
Tue, 24 Jul 2018 12:07:26 +0000 (13:07 +0100)
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>
22 files changed:
doc/openocd.texi
src/helper/log.h
src/target/Makefile.am
src/target/riscv/Makefile.am [new file with mode: 0644]
src/target/riscv/asm.h [new file with mode: 0644]
src/target/riscv/batch.c [new file with mode: 0644]
src/target/riscv/batch.h [new file with mode: 0644]
src/target/riscv/debug_defines.h [new file with mode: 0644]
src/target/riscv/encoding.h [new file with mode: 0644]
src/target/riscv/gdb_regs.h [new file with mode: 0644]
src/target/riscv/opcodes.h [new file with mode: 0644]
src/target/riscv/program.c [new file with mode: 0644]
src/target/riscv/program.h [new file with mode: 0644]
src/target/riscv/riscv-011.c [new file with mode: 0644]
src/target/riscv/riscv-013.c [new file with mode: 0644]
src/target/riscv/riscv.c [new file with mode: 0644]
src/target/riscv/riscv.h [new file with mode: 0644]
src/target/riscv/riscv_semihosting.c [new file with mode: 0644]
src/target/target.c
tcl/board/sifive-e31arty.cfg [new file with mode: 0644]
tcl/board/sifive-e51arty.cfg [new file with mode: 0644]
tcl/board/sifive-hifive1.cfg [new file with mode: 0644]

index 15cf742683f304da2133391704e84a90e82ff791..43ebf8cb28a8b0658b646b354ea1ca3922928f9b 100644 (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
index 512bcc5126d5bd9563c52899f0873d90c2d1462d..d60587f72c4ce3f72cfb38ebd36d2ee7f48786f0 100644 (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 */
index fcc23adbe506946a930dd8349cd72b6dba624e91..b1119e7df0e5fbe3bc2a6d9eb2ce6c7cb4ab2326 100644 (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
diff --git a/src/target/riscv/Makefile.am b/src/target/riscv/Makefile.am
new file mode 100644 (file)
index 0000000..83f1a8c
--- /dev/null
@@ -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
diff --git a/src/target/riscv/asm.h b/src/target/riscv/asm.h
new file mode 100644 (file)
index 0000000..d81aa02
--- /dev/null
@@ -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
diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c
new file mode 100644 (file)
index 0000000..9327cb3
--- /dev/null
@@ -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);
+       }
+}
diff --git a/src/target/riscv/batch.h b/src/target/riscv/batch.h
new file mode 100644 (file)
index 0000000..70690a6
--- /dev/null
@@ -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
diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h
new file mode 100644 (file)
index 0000000..d6ddd4f
--- /dev/null
@@ -0,0 +1,1414 @@
+#define DTM_IDCODE                          0x01
+/*
+* Identifies the release version of this part.
+ */
+#define DTM_IDCODE_VERSION_OFFSET           28
+#define DTM_IDCODE_VERSION_LENGTH           4
+#define DTM_IDCODE_VERSION                  (0xfU << DTM_IDCODE_VERSION_OFFSET)
+/*
+* Identifies the designer's part number of this part.
+ */
+#define DTM_IDCODE_PARTNUMBER_OFFSET        12
+#define DTM_IDCODE_PARTNUMBER_LENGTH        16
+#define DTM_IDCODE_PARTNUMBER               (0xffffU << DTM_IDCODE_PARTNUMBER_OFFSET)
+/*
+* Identifies the designer/manufacturer of this part. Bits 6:0 must be
+* bits 6:0 of the designer/manufacturer's Identification Code as
+* assigned by JEDEC Standard JEP106. Bits 10:7 contain the modulo-16
+* count of the number of continuation characters (0x7f) in that same
+* Identification Code.
+ */
+#define DTM_IDCODE_MANUFID_OFFSET           1
+#define DTM_IDCODE_MANUFID_LENGTH           11
+#define DTM_IDCODE_MANUFID                  (0x7ffU << DTM_IDCODE_MANUFID_OFFSET)
+#define DTM_IDCODE_1_OFFSET                 0
+#define DTM_IDCODE_1_LENGTH                 1
+#define DTM_IDCODE_1                        (0x1U << DTM_IDCODE_1_OFFSET)
+#define DTM_DTMCS                           0x10
+/*
+* Writing 1 to this bit does a hard reset of the DTM,
+* causing the DTM to forget about any outstanding DMI transactions.
+* In general this should only be used when the Debugger has
+* reason to expect that the outstanding DMI transaction will never
+* complete (e.g. a reset condition caused an inflight DMI transaction to
+* be cancelled).
+ */
+#define DTM_DTMCS_DMIHARDRESET_OFFSET       17
+#define DTM_DTMCS_DMIHARDRESET_LENGTH       1
+#define DTM_DTMCS_DMIHARDRESET              (0x1U << DTM_DTMCS_DMIHARDRESET_OFFSET)
+/*
+* Writing 1 to this bit clears the sticky error state
+* and allows the DTM to retry or complete the previous
+* transaction.
+ */
+#define DTM_DTMCS_DMIRESET_OFFSET           16
+#define DTM_DTMCS_DMIRESET_LENGTH           1
+#define DTM_DTMCS_DMIRESET                  (0x1U << DTM_DTMCS_DMIRESET_OFFSET)
+/*
+* This is a hint to the debugger of the minimum number of
+* cycles a debugger should spend in
+* Run-Test/Idle after every DMI scan to avoid a `busy'
+* return code (\Fdmistat of 3). A debugger must still
+* check \Fdmistat when necessary.
+*
+* 0: It is not necessary to enter Run-Test/Idle at all.
+*
+* 1: Enter Run-Test/Idle and leave it immediately.
+*
+* 2: Enter Run-Test/Idle and stay there for 1 cycle before leaving.
+*
+* And so on.
+ */
+#define DTM_DTMCS_IDLE_OFFSET               12
+#define DTM_DTMCS_IDLE_LENGTH               3
+#define DTM_DTMCS_IDLE                      (0x7U << DTM_DTMCS_IDLE_OFFSET)
+/*
+* 0: No error.
+*
+* 1: Reserved. Interpret the same as 2.
+*
+* 2: An operation failed (resulted in \Fop of 2).
+*
+* 3: An operation was attempted while a DMI access was still in
+* progress (resulted in \Fop of 3).
+ */
+#define DTM_DTMCS_DMISTAT_OFFSET            10
+#define DTM_DTMCS_DMISTAT_LENGTH            2
+#define DTM_DTMCS_DMISTAT                   (0x3U << DTM_DTMCS_DMISTAT_OFFSET)
+/*
+* The size of \Faddress in \Rdmi.
+ */
+#define DTM_DTMCS_ABITS_OFFSET              4
+#define DTM_DTMCS_ABITS_LENGTH              6
+#define DTM_DTMCS_ABITS                     (0x3fU << DTM_DTMCS_ABITS_OFFSET)
+/*
+* 0: Version described in spec version 0.11.
+*
+* 1: Version described in spec version 0.13 (and later?), which
+* reduces the DMI data width to 32 bits.
+*
+* 15: Version not described in any available version of this spec.
+ */
+#define DTM_DTMCS_VERSION_OFFSET            0
+#define DTM_DTMCS_VERSION_LENGTH            4
+#define DTM_DTMCS_VERSION                   (0xfU << DTM_DTMCS_VERSION_OFFSET)
+#define DTM_DMI                             0x11
+/*
+* Address used for DMI access. In Update-DR this value is used
+* to access the DM over the DMI.
+ */
+#define DTM_DMI_ADDRESS_OFFSET              34
+#define DTM_DMI_ADDRESS_LENGTH              abits
+#define DTM_DMI_ADDRESS                     (((1L<<abits)-1) << DTM_DMI_ADDRESS_OFFSET)
+/*
+* The data to send to the DM over the DMI during Update-DR, and
+* the data returned from the DM as a result of the previous operation.
+ */
+#define DTM_DMI_DATA_OFFSET                 2
+#define DTM_DMI_DATA_LENGTH                 32
+#define DTM_DMI_DATA                        (0xffffffffULL << DTM_DMI_DATA_OFFSET)
+/*
+* When the debugger writes this field, it has the following meaning:
+*
+* 0: Ignore \Fdata and \Faddress. (nop)
+*
+* Don't send anything over the DMI during Update-DR.
+* This operation should never result in a busy or error response.
+* The address and data reported in the following Capture-DR
+* are undefined.
+*
+* 1: Read from \Faddress. (read)
+*
+* 2: Write \Fdata to \Faddress. (write)
+*
+* 3: Reserved.
+*
+* When the debugger reads this field, it means the following:
+*
+* 0: The previous operation completed successfully.
+*
+* 1: Reserved.
+*
+* 2: A previous operation failed.  The data scanned into \Rdmi in
+* this access will be ignored.  This status is sticky and can be
+* cleared by writing \Fdmireset in \Rdtmcs.
+*
+* This indicates that the DM itself responded with an error.
+* Note: there are no specified cases in which the DM would
+* respond with an error, and DMI is not required to support
+* returning errors.
+*
+* 3: An operation was attempted while a DMI request is still in
+* progress. The data scanned into \Rdmi in this access will be
+* ignored. This status is sticky and can be cleared by writing
+* \Fdmireset in \Rdtmcs. If a debugger sees this status, it
+* needs to give the target more TCK edges between Update-DR and
+* Capture-DR. The simplest way to do that is to add extra transitions
+* in Run-Test/Idle.
+*
+* (The DTM, DM, and/or component may be in different clock domains,
+* so synchronization may be required. Some relatively fixed number of
+* TCK ticks may be needed for the request to reach the DM, complete,
+* and for the response to be synchronized back into the TCK domain.)
+ */
+#define DTM_DMI_OP_OFFSET                   0
+#define DTM_DMI_OP_LENGTH                   2
+#define DTM_DMI_OP                          (0x3ULL << DTM_DMI_OP_OFFSET)
+#define CSR_DCSR                            0x7b0
+/*
+* 0: There is no external debug support.
+*
+* 4: External debug support exists as it is described in this document.
+*
+* 15: There is external debug support, but it does not conform to any
+* available version of this spec.
+ */
+#define CSR_DCSR_XDEBUGVER_OFFSET           28
+#define CSR_DCSR_XDEBUGVER_LENGTH           4
+#define CSR_DCSR_XDEBUGVER                  (0xfU << CSR_DCSR_XDEBUGVER_OFFSET)
+/*
+* When 1, {\tt ebreak} instructions in Machine Mode enter Debug Mode.
+ */
+#define CSR_DCSR_EBREAKM_OFFSET             15
+#define CSR_DCSR_EBREAKM_LENGTH             1
+#define CSR_DCSR_EBREAKM                    (0x1U << CSR_DCSR_EBREAKM_OFFSET)
+/*
+* When 1, {\tt ebreak} instructions in Supervisor Mode enter Debug Mode.
+ */
+#define CSR_DCSR_EBREAKS_OFFSET             13
+#define CSR_DCSR_EBREAKS_LENGTH             1
+#define CSR_DCSR_EBREAKS                    (0x1U << CSR_DCSR_EBREAKS_OFFSET)
+/*
+* When 1, {\tt ebreak} instructions in User/Application Mode enter
+* Debug Mode.
+ */
+#define CSR_DCSR_EBREAKU_OFFSET             12
+#define CSR_DCSR_EBREAKU_LENGTH             1
+#define CSR_DCSR_EBREAKU                    (0x1U << CSR_DCSR_EBREAKU_OFFSET)
+/*
+* 0: Interrupts are disabled during single stepping.
+*
+* 1: Interrupts are enabled during single stepping.
+*
+* Implementations may hard wire this bit to 0.
+* The debugger must read back the value it
+* writes to check whether the feature is supported. If not
+* supported, interrupt behavior can be emulated by the debugger.
+ */
+#define CSR_DCSR_STEPIE_OFFSET              11
+#define CSR_DCSR_STEPIE_LENGTH              1
+#define CSR_DCSR_STEPIE                     (0x1U << CSR_DCSR_STEPIE_OFFSET)
+/*
+* 0: Increment counters as usual.
+*
+* 1: Don't increment any counters while in Debug Mode or on {\tt
+* ebreak} instructions that cause entry into Debug Mode.  These
+* counters include the {\tt cycle} and {\tt instret} CSRs. This is
+* preferred for most debugging scenarios.
+*
+* An implementation may choose not to support writing to this bit.
+* The debugger must read back the value it writes to check whether
+* the feature is supported.
+ */
+#define CSR_DCSR_STOPCOUNT_OFFSET           10
+#define CSR_DCSR_STOPCOUNT_LENGTH           1
+#define CSR_DCSR_STOPCOUNT                  (0x1U << CSR_DCSR_STOPCOUNT_OFFSET)
+/*
+* 0: Increment timers as usual.
+*
+* 1: Don't increment any hart-local timers while in Debug Mode.
+*
+* An implementation may choose not to support writing to this bit.
+* The debugger must read back the value it writes to check whether
+* the feature is supported.
+ */
+#define CSR_DCSR_STOPTIME_OFFSET            9
+#define CSR_DCSR_STOPTIME_LENGTH            1
+#define CSR_DCSR_STOPTIME                   (0x1U << CSR_DCSR_STOPTIME_OFFSET)
+/*
+* Explains why Debug Mode was entered.
+*
+* When there are multiple reasons to enter Debug Mode in a single
+* cycle, hardware should set \Fcause to the cause with the highest
+* priority.
+*
+* 1: An {\tt ebreak} instruction was executed. (priority 3)
+*
+* 2: The Trigger Module caused a breakpoint exception. (priority 4)
+*
+* 3: The debugger requested entry to Debug Mode. (priority 2)
+*
+* 4: The hart single stepped because \Fstep was set. (priority 1)
+*
+* Other values are reserved for future use.
+ */
+#define CSR_DCSR_CAUSE_OFFSET               6
+#define CSR_DCSR_CAUSE_LENGTH               3
+#define CSR_DCSR_CAUSE                      (0x7U << CSR_DCSR_CAUSE_OFFSET)
+/*
+* When 1, \Fmprv in \Rmstatus takes effect during debug mode.
+* When 0, it is ignored during debug mode.
+* Implementing this bit is optional.
+* If not implemented it should be tied to 0.
+ */
+#define CSR_DCSR_MPRVEN_OFFSET              4
+#define CSR_DCSR_MPRVEN_LENGTH              1
+#define CSR_DCSR_MPRVEN                     (0x1U << CSR_DCSR_MPRVEN_OFFSET)
+/*
+* When set, there is a Non-Maskable-Interrupt (NMI) pending for the hart.
+*
+* Since an NMI can indicate a hardware error condition,
+* reliable debugging may no longer be possible once this bit becomes set.
+* This is implementation-dependent.
+ */
+#define CSR_DCSR_NMIP_OFFSET                3
+#define CSR_DCSR_NMIP_LENGTH                1
+#define CSR_DCSR_NMIP                       (0x1U << CSR_DCSR_NMIP_OFFSET)
+/*
+* When set and not in Debug Mode, the hart will only execute a single
+* instruction and then enter Debug Mode.
+* If the instruction does not complete due to an exception,
+* the hart will immediately enter Debug Mode before executing
+* the trap handler, with appropriate exception registers set.
+ */
+#define CSR_DCSR_STEP_OFFSET                2
+#define CSR_DCSR_STEP_LENGTH                1
+#define CSR_DCSR_STEP                       (0x1U << CSR_DCSR_STEP_OFFSET)
+/*
+* Contains the privilege level the hart was operating in when Debug
+* Mode was entered. The encoding is described in Table
+* \ref{tab:privlevel}.  A debugger can change this value to change
+* the hart's privilege level when exiting Debug Mode.
+*
+* Not all privilege levels are supported on all harts. If the
+* encoding written is not supported or the debugger is not allowed to
+* change to it, the hart may change to any supported privilege level.
+ */
+#define CSR_DCSR_PRV_OFFSET                 0
+#define CSR_DCSR_PRV_LENGTH                 2
+#define CSR_DCSR_PRV                        (0x3U << CSR_DCSR_PRV_OFFSET)
+#define CSR_DPC                             0x7b1
+#define CSR_DPC_DPC_OFFSET                  0
+#define CSR_DPC_DPC_LENGTH                  MXLEN
+#define CSR_DPC_DPC                         (((1L<<MXLEN)-1) << CSR_DPC_DPC_OFFSET)
+#define CSR_DSCRATCH0                       0x7b2
+#define CSR_DSCRATCH1                       0x7b3
+#define CSR_TSELECT                         0x7a0
+#define CSR_TSELECT_INDEX_OFFSET            0
+#define CSR_TSELECT_INDEX_LENGTH            MXLEN
+#define CSR_TSELECT_INDEX                   (((1L<<MXLEN)-1) << CSR_TSELECT_INDEX_OFFSET)
+#define CSR_TDATA1                          0x7a1
+/*
+* 0: There is no trigger at this \Rtselect.
+*
+* 1: The trigger is a legacy SiFive address match trigger. These
+* should not be implemented and aren't further documented here.
+*
+* 2: The trigger is an address/data match trigger. The remaining bits
+* in this register act as described in \Rmcontrol.
+*
+* 3: The trigger is an instruction count trigger. The remaining bits
+* in this register act as described in \Ricount.
+*
+* 4: The trigger is an interrupt trigger. The remaining bits
+* in this register act as described in \Ritrigger.
+*
+* 5: The trigger is an exception trigger. The remaining bits
+* in this register act as described in \Retrigger.
+*
+* 15: This trigger exists (so enumeration shouldn't terminate), but
+* is not currently available.
+*
+* Other values are reserved for future use.
+*
+* When this field is written to an unsupported value, it takes on its
+* reset value instead. The reset value is any one of the types
+* supported by the trigger selected by \Rtselect.
+ */
+#define CSR_TDATA1_TYPE_OFFSET              (MXLEN-4)
+#define CSR_TDATA1_TYPE_LENGTH              4
+#define CSR_TDATA1_TYPE                     (0xfULL << CSR_TDATA1_TYPE_OFFSET)
+/*
+* 0: Both Debug and M Mode can write the {\tt tdata} registers at the
+* selected \Rtselect.
+*
+* 1: Only Debug Mode can write the {\tt tdata} registers at the
+* selected \Rtselect.  Writes from other modes are ignored.
+*
+* This bit is only writable from Debug Mode.
+ */
+#define CSR_TDATA1_DMODE_OFFSET             (MXLEN-5)
+#define CSR_TDATA1_DMODE_LENGTH             1
+#define CSR_TDATA1_DMODE                    (0x1ULL << CSR_TDATA1_DMODE_OFFSET)
+/*
+* Trigger-specific data.
+ */
+#define CSR_TDATA1_DATA_OFFSET              0
+#define CSR_TDATA1_DATA_LENGTH              (MXLEN - 5)
+#define CSR_TDATA1_DATA                     (((1L<<MXLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET)
+#define CSR_TDATA2                          0x7a2
+#define CSR_TDATA2_DATA_OFFSET              0
+#define CSR_TDATA2_DATA_LENGTH              MXLEN
+#define CSR_TDATA2_DATA                     (((1L<<MXLEN)-1) << CSR_TDATA2_DATA_OFFSET)
+#define CSR_TDATA3                          0x7a3
+#define CSR_TDATA3_DATA_OFFSET              0
+#define CSR_TDATA3_DATA_LENGTH              MXLEN
+#define CSR_TDATA3_DATA                     (((1L<<MXLEN)-1) << CSR_TDATA3_DATA_OFFSET)
+#define CSR_TINFO                           0x7a4
+/*
+* One bit for each possible \Ftype enumerated in \Rtdataone. Bit N
+* corresponds to type N. If the bit is set, then that type is
+* supported by the currently selected trigger.
+*
+* If the currently selected trigger doesn't exist, this field
+* contains 1.
+*
+* If \Ftype is not writable, this register may be unimplemented, in
+* which case reading it causes an illegal instruction exception. In
+* this case the debugger can read the only supported type from
+* \Rtdataone.
+ */
+#define CSR_TINFO_INFO_OFFSET               0
+#define CSR_TINFO_INFO_LENGTH               16
+#define CSR_TINFO_INFO                      (0xffffULL << CSR_TINFO_INFO_OFFSET)
+#define CSR_MCONTROL                        0x7a1
+#define CSR_MCONTROL_TYPE_OFFSET            (MXLEN-4)
+#define CSR_MCONTROL_TYPE_LENGTH            4
+#define CSR_MCONTROL_TYPE                   (0xfULL << CSR_MCONTROL_TYPE_OFFSET)
+#define CSR_MCONTROL_DMODE_OFFSET           (MXLEN-5)
+#define CSR_MCONTROL_DMODE_LENGTH           1
+#define CSR_MCONTROL_DMODE                  (0x1ULL << CSR_MCONTROL_DMODE_OFFSET)
+/*
+* Specifies the largest naturally aligned powers-of-two (NAPOT) range
+* supported by the hardware when \Fmatch is 1. The value is the
+* logarithm base 2 of the
+* number of bytes in that range.  A value of 0 indicates that only
+* exact value matches are supported (one byte range). A value of 63
+* corresponds to the maximum NAPOT range, which is $2^{63}$ bytes in
+* size.
+ */
+#define CSR_MCONTROL_MASKMAX_OFFSET         (MXLEN-11)
+#define CSR_MCONTROL_MASKMAX_LENGTH         6
+#define CSR_MCONTROL_MASKMAX                (0x3fULL << CSR_MCONTROL_MASKMAX_OFFSET)
+/*
+* If this optional bit is implemented, the hardware sets it when this
+* trigger matches. The trigger's user can set or clear it at any
+* time. The trigger's user can use this bit to determine which
+* trigger(s) matched.  If the bit is not implemented, it is always 0
+* and writing it has no effect.
+ */
+#define CSR_MCONTROL_HIT_OFFSET             20
+#define CSR_MCONTROL_HIT_LENGTH             1
+#define CSR_MCONTROL_HIT                    (0x1ULL << CSR_MCONTROL_HIT_OFFSET)
+/*
+* 0: Perform a match on the virtual address.
+*
+* 1: Perform a match on the data value loaded/stored, or the
+* instruction executed.
+ */
+#define CSR_MCONTROL_SELECT_OFFSET          19
+#define CSR_MCONTROL_SELECT_LENGTH          1
+#define CSR_MCONTROL_SELECT                 (0x1ULL << CSR_MCONTROL_SELECT_OFFSET)
+/*
+* 0: The action for this trigger will be taken just before the
+* instruction that triggered it is executed, but after all preceding
+* instructions are are committed.
+*
+* 1: The action for this trigger will be taken after the instruction
+* that triggered it is executed. It should be taken before the next
+* instruction is executed, but it is better to implement triggers and
+* not implement that suggestion than to not implement them at all.
+*
+* Most hardware will only implement one timing or the other, possibly
+* dependent on \Fselect, \Fexecute, \Fload, and \Fstore. This bit
+* primarily exists for the hardware to communicate to the debugger
+* what will happen. Hardware may implement the bit fully writable, in
+* which case the debugger has a little more control.
+*
+* Data load triggers with \Ftiming of 0 will result in the same load
+* happening again when the debugger lets the hart run. For data load
+* triggers, debuggers must first attempt to set the breakpoint with
+* \Ftiming of 1.
+*
+* A chain of triggers that don't all have the same \Ftiming value
+* will never fire (unless consecutive instructions match the
+* appropriate triggers).
+ */
+#define CSR_MCONTROL_TIMING_OFFSET          18
+#define CSR_MCONTROL_TIMING_LENGTH          1
+#define CSR_MCONTROL_TIMING                 (0x1ULL << CSR_MCONTROL_TIMING_OFFSET)
+/*
+* The action to take when the trigger fires. The values are explained
+* in Table~\ref{tab:action}.
+ */
+#define CSR_MCONTROL_ACTION_OFFSET          12
+#define CSR_MCONTROL_ACTION_LENGTH          6
+#define CSR_MCONTROL_ACTION                 (0x3fULL << CSR_MCONTROL_ACTION_OFFSET)
+/*
+* 0: When this trigger matches, the configured action is taken.
+*
+* 1: While this trigger does not match, it prevents the trigger with
+* the next index from matching.
+*
+* Because \Fchain affects the next trigger, hardware must zero it in
+* writes to \Rmcontrol that set \Fdmode to 0 if the next trigger has
+* \Fdmode of 1.
+* In addition hardware should ignore writes to \Rmcontrol that set
+* \Fdmode to 1 if the previous trigger has both \Fdmode of 0 and
+* \Fchain of 1. Debuggers must avoid the latter case by checking
+* \Fchain on the previous trigger if they're writing \Rmcontrol.
+*
+* Implementations that wish to limit the maximum length of a trigger
+* chain (eg. to meet timing requirements) may do so by zeroing
+* \Fchain in writes to \Rmcontrol that would make the chain too long.
+ */
+#define CSR_MCONTROL_CHAIN_OFFSET           11
+#define CSR_MCONTROL_CHAIN_LENGTH           1
+#define CSR_MCONTROL_CHAIN                  (0x1ULL << CSR_MCONTROL_CHAIN_OFFSET)
+/*
+* 0: Matches when the value equals \Rtdatatwo.
+*
+* 1: Matches when the top M bits of the value match the top M bits of
+* \Rtdatatwo. M is MXLEN-1 minus the index of the least-significant
+* bit containing 0 in \Rtdatatwo.
+*
+* 2: Matches when the value is greater than (unsigned) or equal to
+* \Rtdatatwo.
+*
+* 3: Matches when the value is less than (unsigned) \Rtdatatwo.
+*
+* 4: Matches when the lower half of the value equals the lower half
+* of \Rtdatatwo after the lower half of the value is ANDed with the
+* upper half of \Rtdatatwo.
+*
+* 5: Matches when the upper half of the value equals the lower half
+* of \Rtdatatwo after the upper half of the value is ANDed with the
+* upper half of \Rtdatatwo.
+*
+* Other values are reserved for future use.
+ */
+#define CSR_MCONTROL_MATCH_OFFSET           7
+#define CSR_MCONTROL_MATCH_LENGTH           4
+#define CSR_MCONTROL_MATCH                  (0xfULL << CSR_MCONTROL_MATCH_OFFSET)
+/*
+* When set, enable this trigger in M mode.
+ */
+#define CSR_MCONTROL_M_OFFSET               6
+#define CSR_MCONTROL_M_LENGTH               1
+#define CSR_MCONTROL_M                      (0x1ULL << CSR_MCONTROL_M_OFFSET)
+/*
+* When set, enable this trigger in S mode.
+ */
+#define CSR_MCONTROL_S_OFFSET               4
+#define CSR_MCONTROL_S_LENGTH               1
+#define CSR_MCONTROL_S                      (0x1ULL << CSR_MCONTROL_S_OFFSET)
+/*
+* When set, enable this trigger in U mode.
+ */
+#define CSR_MCONTROL_U_OFFSET               3
+#define CSR_MCONTROL_U_LENGTH               1
+#define CSR_MCONTROL_U                      (0x1ULL << CSR_MCONTROL_U_OFFSET)
+/*
+* When set, the trigger fires on the virtual address or opcode of an
+* instruction that is executed.
+ */
+#define CSR_MCONTROL_EXECUTE_OFFSET         2
+#define CSR_MCONTROL_EXECUTE_LENGTH         1
+#define CSR_MCONTROL_EXECUTE                (0x1ULL << CSR_MCONTROL_EXECUTE_OFFSET)
+/*
+* When set, the trigger fires on the virtual address or data of a store.
+ */
+#define CSR_MCONTROL_STORE_OFFSET           1
+#define CSR_MCONTROL_STORE_LENGTH           1
+#define CSR_MCONTROL_STORE                  (0x1ULL << CSR_MCONTROL_STORE_OFFSET)
+/*
+* When set, the trigger fires on the virtual address or data of a load.
+ */
+#define CSR_MCONTROL_LOAD_OFFSET            0
+#define CSR_MCONTROL_LOAD_LENGTH            1
+#define CSR_MCONTROL_LOAD                   (0x1ULL << CSR_MCONTROL_LOAD_OFFSET)
+#define CSR_ICOUNT                          0x7a1
+#define CSR_ICOUNT_TYPE_OFFSET              (MXLEN-4)
+#define CSR_ICOUNT_TYPE_LENGTH              4
+#define CSR_ICOUNT_TYPE                     (0xfULL << CSR_ICOUNT_TYPE_OFFSET)
+#define CSR_ICOUNT_DMODE_OFFSET             (MXLEN-5)
+#define CSR_ICOUNT_DMODE_LENGTH             1
+#define CSR_ICOUNT_DMODE                    (0x1ULL << CSR_ICOUNT_DMODE_OFFSET)
+/*
+* If this optional bit is implemented, the hardware sets it when this
+* trigger matches. The trigger's user can set or clear it at any
+* time. The trigger's user can use this bit to determine which
+* trigger(s) matched.  If the bit is not implemented, it is always 0
+* and writing it has no effect.
+ */
+#define CSR_ICOUNT_HIT_OFFSET               24
+#define CSR_ICOUNT_HIT_LENGTH               1
+#define CSR_ICOUNT_HIT                      (0x1ULL << CSR_ICOUNT_HIT_OFFSET)
+/*
+* When count is decremented to 0, the trigger fires. Instead of
+* changing \Fcount from 1 to 0, it is also acceptable for hardware to
+* clear \Fm, \Fs, and \Fu. This allows \Fcount to be hard-wired
+* to 1 if this register just exists for single step.
+ */
+#define CSR_ICOUNT_COUNT_OFFSET             10
+#define CSR_ICOUNT_COUNT_LENGTH             14
+#define CSR_ICOUNT_COUNT                    (0x3fffULL << CSR_ICOUNT_COUNT_OFFSET)
+/*
+* When set, every instruction completed or exception taken in M mode decrements \Fcount
+* by 1.
+ */
+#define CSR_ICOUNT_M_OFFSET                 9
+#define CSR_ICOUNT_M_LENGTH                 1
+#define CSR_ICOUNT_M                        (0x1ULL << CSR_ICOUNT_M_OFFSET)
+/*
+* When set, every instruction completed or exception taken in S mode decrements \Fcount
+* by 1.
+ */
+#define CSR_ICOUNT_S_OFFSET                 7
+#define CSR_ICOUNT_S_LENGTH                 1
+#define CSR_ICOUNT_S                        (0x1ULL << CSR_ICOUNT_S_OFFSET)
+/*
+* When set, every instruction completed or exception taken in U mode decrements \Fcount
+* by 1.
+ */
+#define CSR_ICOUNT_U_OFFSET                 6
+#define CSR_ICOUNT_U_LENGTH                 1
+#define CSR_ICOUNT_U                        (0x1ULL << CSR_ICOUNT_U_OFFSET)
+/*
+* The action to take when the trigger fires. The values are explained
+* in Table~\ref{tab:action}.
+ */
+#define CSR_ICOUNT_ACTION_OFFSET            0
+#define CSR_ICOUNT_ACTION_LENGTH            6
+#define CSR_ICOUNT_ACTION                   (0x3fULL << CSR_ICOUNT_ACTION_OFFSET)
+#define CSR_ITRIGGER                        0x7a1
+#define CSR_ITRIGGER_TYPE_OFFSET            (MXLEN-4)
+#define CSR_ITRIGGER_TYPE_LENGTH            4
+#define CSR_ITRIGGER_TYPE                   (0xfULL << CSR_ITRIGGER_TYPE_OFFSET)
+#define CSR_ITRIGGER_DMODE_OFFSET           (MXLEN-5)
+#define CSR_ITRIGGER_DMODE_LENGTH           1
+#define CSR_ITRIGGER_DMODE                  (0x1ULL << CSR_ITRIGGER_DMODE_OFFSET)
+/*
+* If this optional bit is implemented, the hardware sets it when this
+* trigger matches. The trigger's user can set or clear it at any
+* time. The trigger's user can use this bit to determine which
+* trigger(s) matched.  If the bit is not implemented, it is always 0
+* and writing it has no effect.
+ */
+#define CSR_ITRIGGER_HIT_OFFSET             (MXLEN-6)
+#define CSR_ITRIGGER_HIT_LENGTH             1
+#define CSR_ITRIGGER_HIT                    (0x1ULL << CSR_ITRIGGER_HIT_OFFSET)
+/*
+* When set, enable this trigger for interrupts that are taken from M
+* mode.
+ */
+#define CSR_ITRIGGER_M_OFFSET               9
+#define CSR_ITRIGGER_M_LENGTH               1
+#define CSR_ITRIGGER_M                      (0x1ULL << CSR_ITRIGGER_M_OFFSET)
+/*
+* When set, enable this trigger for interrupts that are taken from S
+* mode.
+ */
+#define CSR_ITRIGGER_S_OFFSET               7
+#define CSR_ITRIGGER_S_LENGTH               1
+#define CSR_ITRIGGER_S                      (0x1ULL << CSR_ITRIGGER_S_OFFSET)
+/*
+* When set, enable this trigger for interrupts that are taken from U
+* mode.
+ */
+#define CSR_ITRIGGER_U_OFFSET               6
+#define CSR_ITRIGGER_U_LENGTH               1
+#define CSR_ITRIGGER_U                      (0x1ULL << CSR_ITRIGGER_U_OFFSET)
+/*
+* The action to take when the trigger fires. The values are explained
+* in Table~\ref{tab:action}.
+ */
+#define CSR_ITRIGGER_ACTION_OFFSET          0
+#define CSR_ITRIGGER_ACTION_LENGTH          6
+#define CSR_ITRIGGER_ACTION                 (0x3fULL << CSR_ITRIGGER_ACTION_OFFSET)
+#define CSR_ETRIGGER                        0x7a1
+#define CSR_ETRIGGER_TYPE_OFFSET            (MXLEN-4)
+#define CSR_ETRIGGER_TYPE_LENGTH            4
+#define CSR_ETRIGGER_TYPE                   (0xfULL << CSR_ETRIGGER_TYPE_OFFSET)
+#define CSR_ETRIGGER_DMODE_OFFSET           (MXLEN-5)
+#define CSR_ETRIGGER_DMODE_LENGTH           1
+#define CSR_ETRIGGER_DMODE                  (0x1ULL << CSR_ETRIGGER_DMODE_OFFSET)
+/*
+* If this optional bit is implemented, the hardware sets it when this
+* trigger matches. The trigger's user can set or clear it at any
+* time. The trigger's user can use this bit to determine which
+* trigger(s) matched.  If the bit is not implemented, it is always 0
+* and writing it has no effect.
+ */
+#define CSR_ETRIGGER_HIT_OFFSET             (MXLEN-6)
+#define CSR_ETRIGGER_HIT_LENGTH             1
+#define CSR_ETRIGGER_HIT                    (0x1ULL << CSR_ETRIGGER_HIT_OFFSET)
+/*
+* When set, enable this trigger for exceptions that are taken from M
+* mode.
+ */
+#define CSR_ETRIGGER_M_OFFSET               9
+#define CSR_ETRIGGER_M_LENGTH               1
+#define CSR_ETRIGGER_M                      (0x1ULL << CSR_ETRIGGER_M_OFFSET)
+/*
+* When set, enable this trigger for exceptions that are taken from S
+* mode.
+ */
+#define CSR_ETRIGGER_S_OFFSET               7
+#define CSR_ETRIGGER_S_LENGTH               1
+#define CSR_ETRIGGER_S                      (0x1ULL << CSR_ETRIGGER_S_OFFSET)
+/*
+* When set, enable this trigger for exceptions that are taken from U
+* mode.
+ */
+#define CSR_ETRIGGER_U_OFFSET               6
+#define CSR_ETRIGGER_U_LENGTH               1
+#define CSR_ETRIGGER_U                      (0x1ULL << CSR_ETRIGGER_U_OFFSET)
+/*
+* The action to take when the trigger fires. The values are explained
+* in Table~\ref{tab:action}.
+ */
+#define CSR_ETRIGGER_ACTION_OFFSET          0
+#define CSR_ETRIGGER_ACTION_LENGTH          6
+#define CSR_ETRIGGER_ACTION                 (0x3fULL << CSR_ETRIGGER_ACTION_OFFSET)
+#define DMI_DMSTATUS                        0x11
+/*
+* If 1, then there is an implicit {\tt ebreak} instruction at the
+* non-existent word immediately after the Program Buffer. This saves
+* the debugger from having to write the {\tt ebreak} itself, and
+* allows the Program Buffer to be one word smaller.
+*
+* This must be 1 when \Fprogbufsize is 1.
+ */
+#define DMI_DMSTATUS_IMPEBREAK_OFFSET       22
+#define DMI_DMSTATUS_IMPEBREAK_LENGTH       1
+#define DMI_DMSTATUS_IMPEBREAK              (0x1U << DMI_DMSTATUS_IMPEBREAK_OFFSET)
+/*
+* This field is 1 when all currently selected harts have been reset but the reset has not been acknowledged.
+ */
+#define DMI_DMSTATUS_ALLHAVERESET_OFFSET    19
+#define DMI_DMSTATUS_ALLHAVERESET_LENGTH    1
+#define DMI_DMSTATUS_ALLHAVERESET           (0x1U << DMI_DMSTATUS_ALLHAVERESET_OFFSET)
+/*
+* This field is 1 when any currently selected hart has been reset but the reset has not been acknowledged.
+ */
+#define DMI_DMSTATUS_ANYHAVERESET_OFFSET    18
+#define DMI_DMSTATUS_ANYHAVERESET_LENGTH    1
+#define DMI_DMSTATUS_ANYHAVERESET           (0x1U << DMI_DMSTATUS_ANYHAVERESET_OFFSET)
+/*
+* This field is 1 when all currently selected harts have acknowledged
+* the previous resume request.
+ */
+#define DMI_DMSTATUS_ALLRESUMEACK_OFFSET    17
+#define DMI_DMSTATUS_ALLRESUMEACK_LENGTH    1
+#define DMI_DMSTATUS_ALLRESUMEACK           (0x1U << DMI_DMSTATUS_ALLRESUMEACK_OFFSET)
+/*
+* This field is 1 when any currently selected hart has acknowledged
+* the previous resume request.
+ */
+#define DMI_DMSTATUS_ANYRESUMEACK_OFFSET    16
+#define DMI_DMSTATUS_ANYRESUMEACK_LENGTH    1
+#define DMI_DMSTATUS_ANYRESUMEACK           (0x1U << DMI_DMSTATUS_ANYRESUMEACK_OFFSET)
+/*
+* This field is 1 when all currently selected harts do not exist in this system.
+ */
+#define DMI_DMSTATUS_ALLNONEXISTENT_OFFSET  15
+#define DMI_DMSTATUS_ALLNONEXISTENT_LENGTH  1
+#define DMI_DMSTATUS_ALLNONEXISTENT         (0x1U << DMI_DMSTATUS_ALLNONEXISTENT_OFFSET)
+/*
+* This field is 1 when any currently selected hart does not exist in this system.
+ */
+#define DMI_DMSTATUS_ANYNONEXISTENT_OFFSET  14
+#define DMI_DMSTATUS_ANYNONEXISTENT_LENGTH  1
+#define DMI_DMSTATUS_ANYNONEXISTENT         (0x1U << DMI_DMSTATUS_ANYNONEXISTENT_OFFSET)
+/*
+* This field is 1 when all currently selected harts are unavailable.
+ */
+#define DMI_DMSTATUS_ALLUNAVAIL_OFFSET      13
+#define DMI_DMSTATUS_ALLUNAVAIL_LENGTH      1
+#define DMI_DMSTATUS_ALLUNAVAIL             (0x1U << DMI_DMSTATUS_ALLUNAVAIL_OFFSET)
+/*
+* This field is 1 when any currently selected hart is unavailable.
+ */
+#define DMI_DMSTATUS_ANYUNAVAIL_OFFSET      12
+#define DMI_DMSTATUS_ANYUNAVAIL_LENGTH      1
+#define DMI_DMSTATUS_ANYUNAVAIL             (0x1U << DMI_DMSTATUS_ANYUNAVAIL_OFFSET)
+/*
+* This field is 1 when all currently selected harts are running.
+ */
+#define DMI_DMSTATUS_ALLRUNNING_OFFSET      11
+#define DMI_DMSTATUS_ALLRUNNING_LENGTH      1
+#define DMI_DMSTATUS_ALLRUNNING             (0x1U << DMI_DMSTATUS_ALLRUNNING_OFFSET)
+/*
+* This field is 1 when any currently selected hart is running.
+ */
+#define DMI_DMSTATUS_ANYRUNNING_OFFSET      10
+#define DMI_DMSTATUS_ANYRUNNING_LENGTH      1
+#define DMI_DMSTATUS_ANYRUNNING             (0x1U << DMI_DMSTATUS_ANYRUNNING_OFFSET)
+/*
+* This field is 1 when all currently selected harts are halted.
+ */
+#define DMI_DMSTATUS_ALLHALTED_OFFSET       9
+#define DMI_DMSTATUS_ALLHALTED_LENGTH       1
+#define DMI_DMSTATUS_ALLHALTED              (0x1U << DMI_DMSTATUS_ALLHALTED_OFFSET)
+/*
+* This field is 1 when any currently selected hart is halted.
+ */
+#define DMI_DMSTATUS_ANYHALTED_OFFSET       8
+#define DMI_DMSTATUS_ANYHALTED_LENGTH       1
+#define DMI_DMSTATUS_ANYHALTED              (0x1U << DMI_DMSTATUS_ANYHALTED_OFFSET)
+/*
+* 0 when authentication is required before using the DM.  1 when the
+* authentication check has passed. On components that don't implement
+* authentication, this bit must be preset as 1.
+ */
+#define DMI_DMSTATUS_AUTHENTICATED_OFFSET   7
+#define DMI_DMSTATUS_AUTHENTICATED_LENGTH   1
+#define DMI_DMSTATUS_AUTHENTICATED          (0x1U << DMI_DMSTATUS_AUTHENTICATED_OFFSET)
+/*
+* 0: The authentication module is ready to process the next
+* read/write to \Rauthdata.
+*
+* 1: The authentication module is busy. Accessing \Rauthdata results
+* in unspecified behavior.
+*
+* \Fauthbusy only becomes set in immediate response to an access to
+* \Rauthdata.
+ */
+#define DMI_DMSTATUS_AUTHBUSY_OFFSET        6
+#define DMI_DMSTATUS_AUTHBUSY_LENGTH        1
+#define DMI_DMSTATUS_AUTHBUSY               (0x1U << DMI_DMSTATUS_AUTHBUSY_OFFSET)
+/*
+* 1 if this Debug Module supports halt-on-reset functionality
+* controllable by the \Fsetresethaltreq and \Fclrresethaltreq bits.
+* 0 otherwise.
+ */
+#define DMI_DMSTATUS_HASRESETHALTREQ_OFFSET 5
+#define DMI_DMSTATUS_HASRESETHALTREQ_LENGTH 1
+#define DMI_DMSTATUS_HASRESETHALTREQ        (0x1U << DMI_DMSTATUS_HASRESETHALTREQ_OFFSET)
+/*
+* 0: \Rdevtreeaddrzero--\Rdevtreeaddrthree hold information which
+* is not relevant to the Device Tree.
+*
+* 1: \Rdevtreeaddrzero--\Rdevtreeaddrthree registers hold the address of the
+* Device Tree.
+ */
+#define DMI_DMSTATUS_DEVTREEVALID_OFFSET    4
+#define DMI_DMSTATUS_DEVTREEVALID_LENGTH    1
+#define DMI_DMSTATUS_DEVTREEVALID           (0x1U << DMI_DMSTATUS_DEVTREEVALID_OFFSET)
+/*
+* 0: There is no Debug Module present.
+*
+* 1: There is a Debug Module and it conforms to version 0.11 of this
+* specification.
+*
+* 2: There is a Debug Module and it conforms to version 0.13 of this
+* specification.
+*
+* 15: There is a Debug Module but it does not conform to any
+* available version of this spec.
+ */
+#define DMI_DMSTATUS_VERSION_OFFSET         0
+#define DMI_DMSTATUS_VERSION_LENGTH         4
+#define DMI_DMSTATUS_VERSION                (0xfU << DMI_DMSTATUS_VERSION_OFFSET)
+#define DMI_DMCONTROL                       0x10
+/*
+* Writes the halt request bit for all currently selected harts.
+* When set to 1, each selected hart will halt if it is not currently
+* halted.
+*
+* Writing 1 or 0 has no effect on a hart which is already halted, but
+* the bit must be cleared to 0 before the hart is resumed.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+ */
+#define DMI_DMCONTROL_HALTREQ_OFFSET        31
+#define DMI_DMCONTROL_HALTREQ_LENGTH        1
+#define DMI_DMCONTROL_HALTREQ               (0x1U << DMI_DMCONTROL_HALTREQ_OFFSET)
+/*
+* Writes the resume request bit for all currently selected harts.
+* When set to 1, each selected hart will resume if it is currently
+* halted.
+*
+* The resume request bit is ignored while the halt request bit is
+* set.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+ */
+#define DMI_DMCONTROL_RESUMEREQ_OFFSET      30
+#define DMI_DMCONTROL_RESUMEREQ_LENGTH      1
+#define DMI_DMCONTROL_RESUMEREQ             (0x1U << DMI_DMCONTROL_RESUMEREQ_OFFSET)
+/*
+* This optional field writes the reset bit for all the currently
+* selected harts.  To perform a reset the debugger writes 1, and then
+* writes 0 to deassert the reset signal.
+*
+* If this feature is not implemented, the bit always stays 0, so
+* after writing 1 the debugger can read the register back to see if
+* the feature is supported.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+ */
+#define DMI_DMCONTROL_HARTRESET_OFFSET      29
+#define DMI_DMCONTROL_HARTRESET_LENGTH      1
+#define DMI_DMCONTROL_HARTRESET             (0x1U << DMI_DMCONTROL_HARTRESET_OFFSET)
+/*
+* Writing 1 to this bit clears the {\tt havereset} bits for
+* any selected harts.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+ */
+#define DMI_DMCONTROL_ACKHAVERESET_OFFSET   28
+#define DMI_DMCONTROL_ACKHAVERESET_LENGTH   1
+#define DMI_DMCONTROL_ACKHAVERESET          (0x1U << DMI_DMCONTROL_ACKHAVERESET_OFFSET)
+/*
+* Selects the  definition of currently selected harts.
+*
+* 0: There is a single currently selected hart, that selected by \Fhartsel.
+*
+* 1: There may be multiple currently selected harts -- that selected by \Fhartsel,
+* plus those selected by the hart array mask register.
+*
+* An implementation which does not implement the hart array mask register
+* must tie this field to 0. A debugger which wishes to use the hart array
+* mask register feature should set this bit and read back to see if the functionality
+* is supported.
+ */
+#define DMI_DMCONTROL_HASEL_OFFSET          26
+#define DMI_DMCONTROL_HASEL_LENGTH          1
+#define DMI_DMCONTROL_HASEL                 (0x1U << DMI_DMCONTROL_HASEL_OFFSET)
+/*
+* The low 10 bits of \Fhartsel: the DM-specific index of the hart to
+* select. This hart is always part of the currently selected harts.
+ */
+#define DMI_DMCONTROL_HARTSELLO_OFFSET      16
+#define DMI_DMCONTROL_HARTSELLO_LENGTH      10
+#define DMI_DMCONTROL_HARTSELLO             (0x3ffU << DMI_DMCONTROL_HARTSELLO_OFFSET)
+/*
+* The high 10 bits of \Fhartsel: the DM-specific index of the hart to
+* select. This hart is always part of the currently selected harts.
+ */
+#define DMI_DMCONTROL_HARTSELHI_OFFSET      6
+#define DMI_DMCONTROL_HARTSELHI_LENGTH      10
+#define DMI_DMCONTROL_HARTSELHI             (0x3ffU << DMI_DMCONTROL_HARTSELHI_OFFSET)
+/*
+* This optional field writes the halt-on-reset request bit for all
+* currently selected harts.
+* When set to 1, each selected hart will halt upon the next deassertion
+* of its reset. The halt-on-reset request bit is not automatically
+* cleared. The debugger must write to \Fclrresethaltreq to clear it.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+*
+* If \Fhasresethaltreq is 0, this field is not implemented.
+ */
+#define DMI_DMCONTROL_SETRESETHALTREQ_OFFSET 3
+#define DMI_DMCONTROL_SETRESETHALTREQ_LENGTH 1
+#define DMI_DMCONTROL_SETRESETHALTREQ       (0x1U << DMI_DMCONTROL_SETRESETHALTREQ_OFFSET)
+/*
+* This optional field clears the halt-on-reset request bit for all
+* currently selected harts.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+ */
+#define DMI_DMCONTROL_CLRRESETHALTREQ_OFFSET 2
+#define DMI_DMCONTROL_CLRRESETHALTREQ_LENGTH 1
+#define DMI_DMCONTROL_CLRRESETHALTREQ       (0x1U << DMI_DMCONTROL_CLRRESETHALTREQ_OFFSET)
+/*
+* This bit controls the reset signal from the DM to the rest of the
+* system. The signal should reset every part of the system, including
+* every hart, except for the DM and any logic required to access the
+* DM.
+* To perform a system reset the debugger writes 1,
+* and then writes 0
+* to deassert the reset.
+ */
+#define DMI_DMCONTROL_NDMRESET_OFFSET       1
+#define DMI_DMCONTROL_NDMRESET_LENGTH       1
+#define DMI_DMCONTROL_NDMRESET              (0x1U << DMI_DMCONTROL_NDMRESET_OFFSET)
+/*
+* This bit serves as a reset signal for the Debug Module itself.
+*
+* 0: The module's state, including authentication mechanism,
+* takes its reset values (the \Fdmactive bit is the only bit which can
+* be written to something other than its reset value).
+*
+* 1: The module functions normally.
+*
+* No other mechanism should exist that may result in resetting the
+* Debug Module after power up, including the platform's system reset
+* or Debug Transport reset signals.
+*
+* A debugger may pulse this bit low to get the Debug Module into a
+* known state.
+*
+* Implementations may use this bit to aid debugging, for example by
+* preventing the Debug Module from being power gated while debugging
+* is active.
+ */
+#define DMI_DMCONTROL_DMACTIVE_OFFSET       0
+#define DMI_DMCONTROL_DMACTIVE_LENGTH       1
+#define DMI_DMCONTROL_DMACTIVE              (0x1U << DMI_DMCONTROL_DMACTIVE_OFFSET)
+#define DMI_HARTINFO                        0x12
+/*
+* Number of {\tt dscratch} registers available for the debugger
+* to use during program buffer execution, starting from \Rdscratchzero.
+* The debugger can make no assumptions about the contents of these
+* registers between commands.
+ */
+#define DMI_HARTINFO_NSCRATCH_OFFSET        20
+#define DMI_HARTINFO_NSCRATCH_LENGTH        4
+#define DMI_HARTINFO_NSCRATCH               (0xfU << DMI_HARTINFO_NSCRATCH_OFFSET)
+/*
+* 0: The {\tt data} registers are shadowed in the hart by CSR
+* registers. Each CSR register is MXLEN bits in size, and corresponds
+* to a single argument, per Table~\ref{tab:datareg}.
+*
+* 1: The {\tt data} registers are shadowed in the hart's memory map.
+* Each register takes up 4 bytes in the memory map.
+ */
+#define DMI_HARTINFO_DATAACCESS_OFFSET      16
+#define DMI_HARTINFO_DATAACCESS_LENGTH      1
+#define DMI_HARTINFO_DATAACCESS             (0x1U << DMI_HARTINFO_DATAACCESS_OFFSET)
+/*
+* If \Fdataaccess is 0: Number of CSR registers dedicated to
+* shadowing the {\tt data} registers.
+*
+* If \Fdataaccess is 1: Number of 32-bit words in the memory map
+* dedicated to shadowing the {\tt data} registers.
+*
+* Since there are at most 12 {\tt data} registers, the value in this
+* register must be 12 or smaller.
+ */
+#define DMI_HARTINFO_DATASIZE_OFFSET        12
+#define DMI_HARTINFO_DATASIZE_LENGTH        4
+#define DMI_HARTINFO_DATASIZE               (0xfU << DMI_HARTINFO_DATASIZE_OFFSET)
+/*
+* If \Fdataaccess is 0: The number of the first CSR dedicated to
+* shadowing the {\tt data} registers.
+*
+* If \Fdataaccess is 1: Signed address of RAM where the {\tt data}
+* registers are shadowed, to be used to access relative to \Rzero.
+ */
+#define DMI_HARTINFO_DATAADDR_OFFSET        0
+#define DMI_HARTINFO_DATAADDR_LENGTH        12
+#define DMI_HARTINFO_DATAADDR               (0xfffU << DMI_HARTINFO_DATAADDR_OFFSET)
+#define DMI_HAWINDOWSEL                     0x14
+/*
+* The high bits of this field may be tied to 0, depending on how large
+* the array mask register is.  Eg. on a system with 48 harts only bit 0
+* of this field may actually be writable.
+ */
+#define DMI_HAWINDOWSEL_HAWINDOWSEL_OFFSET  0
+#define DMI_HAWINDOWSEL_HAWINDOWSEL_LENGTH  15
+#define DMI_HAWINDOWSEL_HAWINDOWSEL         (0x7fffU << DMI_HAWINDOWSEL_HAWINDOWSEL_OFFSET)
+#define DMI_HAWINDOW                        0x15
+#define DMI_HAWINDOW_MASKDATA_OFFSET        0
+#define DMI_HAWINDOW_MASKDATA_LENGTH        32
+#define DMI_HAWINDOW_MASKDATA               (0xffffffffU << DMI_HAWINDOW_MASKDATA_OFFSET)
+#define DMI_ABSTRACTCS                      0x16
+/*
+* Size of the Program Buffer, in 32-bit words. Valid sizes are 0 - 16.
+ */
+#define DMI_ABSTRACTCS_PROGBUFSIZE_OFFSET   24
+#define DMI_ABSTRACTCS_PROGBUFSIZE_LENGTH   5
+#define DMI_ABSTRACTCS_PROGBUFSIZE          (0x1fU << DMI_ABSTRACTCS_PROGBUFSIZE_OFFSET)
+/*
+* 1: An abstract command is currently being executed.
+*
+* This bit is set as soon as \Rcommand is written, and is
+* not cleared until that command has completed.
+ */
+#define DMI_ABSTRACTCS_BUSY_OFFSET          12
+#define DMI_ABSTRACTCS_BUSY_LENGTH          1
+#define DMI_ABSTRACTCS_BUSY                 (0x1U << DMI_ABSTRACTCS_BUSY_OFFSET)
+/*
+* Gets set if an abstract command fails. The bits in this field remain set until
+* they are cleared by writing 1 to them. No abstract command is
+* started until the value is reset to 0.
+*
+* 0 (none): No error.
+*
+* 1 (busy): An abstract command was executing while \Rcommand,
+* \Rabstractcs, \Rabstractauto was written, or when one
+* of the {\tt data} or {\tt progbuf} registers was read or written.
+*
+* 2 (not supported): The requested command is not supported,
+* regardless of whether the hart is running or not.
+*
+* 3 (exception): An exception occurred while executing the command
+* (eg. while executing the Program Buffer).
+*
+* 4 (halt/resume): The abstract command couldn't execute because the
+* hart wasn't in the required state (running/halted).
+*
+* 7 (other): The command failed for another reason.
+ */
+#define DMI_ABSTRACTCS_CMDERR_OFFSET        8
+#define DMI_ABSTRACTCS_CMDERR_LENGTH        3
+#define DMI_ABSTRACTCS_CMDERR               (0x7U << DMI_ABSTRACTCS_CMDERR_OFFSET)
+/*
+* Number of {\tt data} registers that are implemented as part of the
+* abstract command interface. Valid sizes are 0 - 12.
+ */
+#define DMI_ABSTRACTCS_DATACOUNT_OFFSET     0
+#define DMI_ABSTRACTCS_DATACOUNT_LENGTH     4
+#define DMI_ABSTRACTCS_DATACOUNT            (0xfU << DMI_ABSTRACTCS_DATACOUNT_OFFSET)
+#define DMI_COMMAND                         0x17
+/*
+* The type determines the overall functionality of this
+* abstract command.
+ */
+#define DMI_COMMAND_CMDTYPE_OFFSET          24
+#define DMI_COMMAND_CMDTYPE_LENGTH          8
+#define DMI_COMMAND_CMDTYPE                 (0xffU << DMI_COMMAND_CMDTYPE_OFFSET)
+/*
+* This field is interpreted in a command-specific manner,
+* described for each abstract command.
+ */
+#define DMI_COMMAND_CONTROL_OFFSET          0
+#define DMI_COMMAND_CONTROL_LENGTH          24
+#define DMI_COMMAND_CONTROL                 (0xffffffU << DMI_COMMAND_CONTROL_OFFSET)
+#define DMI_ABSTRACTAUTO                    0x18
+/*
+* When a bit in this field is 1, read or write accesses to the corresponding {\tt progbuf} word
+* cause the command in \Rcommand to be executed again.
+ */
+#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET 16
+#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_LENGTH 16
+#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF    (0xffffU << DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET)
+/*
+* When a bit in this field is 1, read or write accesses to the corresponding {\tt data} word
+* cause the command in \Rcommand to be executed again.
+ */
+#define DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET 0
+#define DMI_ABSTRACTAUTO_AUTOEXECDATA_LENGTH 12
+#define DMI_ABSTRACTAUTO_AUTOEXECDATA       (0xfffU << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET)
+#define DMI_DEVTREEADDR0                    0x19
+#define DMI_DEVTREEADDR0_ADDR_OFFSET        0
+#define DMI_DEVTREEADDR0_ADDR_LENGTH        32
+#define DMI_DEVTREEADDR0_ADDR               (0xffffffffU << DMI_DEVTREEADDR0_ADDR_OFFSET)
+#define DMI_DEVTREEADDR1                    0x1a
+#define DMI_DEVTREEADDR2                    0x1b
+#define DMI_DEVTREEADDR3                    0x1c
+#define DMI_NEXTDM                          0x1d
+#define DMI_NEXTDM_ADDR_OFFSET              0
+#define DMI_NEXTDM_ADDR_LENGTH              32
+#define DMI_NEXTDM_ADDR                     (0xffffffffU << DMI_NEXTDM_ADDR_OFFSET)
+#define DMI_DATA0                           0x04
+#define DMI_DATA0_DATA_OFFSET               0
+#define DMI_DATA0_DATA_LENGTH               32
+#define DMI_DATA0_DATA                      (0xffffffffU << DMI_DATA0_DATA_OFFSET)
+#define DMI_DATA11                          0x0f
+#define DMI_PROGBUF0                        0x20
+#define DMI_PROGBUF0_DATA_OFFSET            0
+#define DMI_PROGBUF0_DATA_LENGTH            32
+#define DMI_PROGBUF0_DATA                   (0xffffffffU << DMI_PROGBUF0_DATA_OFFSET)
+#define DMI_PROGBUF15                       0x2f
+#define DMI_AUTHDATA                        0x30
+#define DMI_AUTHDATA_DATA_OFFSET            0
+#define DMI_AUTHDATA_DATA_LENGTH            32
+#define DMI_AUTHDATA_DATA                   (0xffffffffU << DMI_AUTHDATA_DATA_OFFSET)
+#define DMI_HALTSUM0                        0x40
+#define DMI_HALTSUM0_HALTSUM0_OFFSET        0
+#define DMI_HALTSUM0_HALTSUM0_LENGTH        32
+#define DMI_HALTSUM0_HALTSUM0               (0xffffffffU << DMI_HALTSUM0_HALTSUM0_OFFSET)
+#define DMI_HALTSUM1                        0x13
+#define DMI_HALTSUM1_HALTSUM1_OFFSET        0
+#define DMI_HALTSUM1_HALTSUM1_LENGTH        32
+#define DMI_HALTSUM1_HALTSUM1               (0xffffffffU << DMI_HALTSUM1_HALTSUM1_OFFSET)
+#define DMI_HALTSUM2                        0x34
+#define DMI_HALTSUM2_HALTSUM2_OFFSET        0
+#define DMI_HALTSUM2_HALTSUM2_LENGTH        32
+#define DMI_HALTSUM2_HALTSUM2               (0xffffffffU << DMI_HALTSUM2_HALTSUM2_OFFSET)
+#define DMI_HALTSUM3                        0x35
+#define DMI_HALTSUM3_HALTSUM3_OFFSET        0
+#define DMI_HALTSUM3_HALTSUM3_LENGTH        32
+#define DMI_HALTSUM3_HALTSUM3               (0xffffffffU << DMI_HALTSUM3_HALTSUM3_OFFSET)
+#define DMI_SBADDRESS3                      0x37
+/*
+* Accesses bits 127:96 of the physical address in {\tt sbaddress} (if
+* the system address bus is that wide).
+ */
+#define DMI_SBADDRESS3_ADDRESS_OFFSET       0
+#define DMI_SBADDRESS3_ADDRESS_LENGTH       32
+#define DMI_SBADDRESS3_ADDRESS              (0xffffffffU << DMI_SBADDRESS3_ADDRESS_OFFSET)
+#define DMI_SBCS                            0x38
+/*
+* 0: The System Bus interface conforms to mainline drafts of this
+* spec older than 1 January, 2018.
+*
+* 1: The System Bus interface conforms to this version of the spec.
+*
+* Other values are reserved for future versions.
+ */
+#define DMI_SBCS_SBVERSION_OFFSET           29
+#define DMI_SBCS_SBVERSION_LENGTH           3
+#define DMI_SBCS_SBVERSION                  (0x7U << DMI_SBCS_SBVERSION_OFFSET)
+/*
+* Set when the debugger attempts to read data while a read is in
+* progress, or when the debugger initiates a new access while one is
+* already in progress (while \Fsbbusy is set). It remains set until
+* it's explicitly cleared by the debugger.
+*
+* While this field is non-zero, no more system bus accesses can be
+* initiated by the Debug Module.
+ */
+#define DMI_SBCS_SBBUSYERROR_OFFSET         22
+#define DMI_SBCS_SBBUSYERROR_LENGTH         1
+#define DMI_SBCS_SBBUSYERROR                (0x1U << DMI_SBCS_SBBUSYERROR_OFFSET)
+/*
+* When 1, indicates the system bus master is busy. (Whether the
+* system bus itself is busy is related, but not the same thing.) This
+* bit goes high immediately when a read or write is requested for any
+* reason, and does not go low until the access is fully completed.
+*
+* Writes to \Rsbcs while \Fsbbusy is high result in undefined
+* behavior.  A debugger must not write to \Rsbcs until it reads
+* \Fsbbusy as 0.
+ */
+#define DMI_SBCS_SBBUSY_OFFSET              21
+#define DMI_SBCS_SBBUSY_LENGTH              1
+#define DMI_SBCS_SBBUSY                     (0x1U << DMI_SBCS_SBBUSY_OFFSET)
+/*
+* When 1, every write to \Rsbaddresszero automatically triggers a
+* system bus read at the new address.
+ */
+#define DMI_SBCS_SBREADONADDR_OFFSET        20
+#define DMI_SBCS_SBREADONADDR_LENGTH        1
+#define DMI_SBCS_SBREADONADDR               (0x1U << DMI_SBCS_SBREADONADDR_OFFSET)
+/*
+* Select the access size to use for system bus accesses.
+*
+* 0: 8-bit
+*
+* 1: 16-bit
+*
+* 2: 32-bit
+*
+* 3: 64-bit
+*
+* 4: 128-bit
+*
+* If \Fsbaccess has an unsupported value when the DM starts a bus
+* access, the access is not performed and \Fsberror is set to 3.
+ */
+#define DMI_SBCS_SBACCESS_OFFSET            17
+#define DMI_SBCS_SBACCESS_LENGTH            3
+#define DMI_SBCS_SBACCESS                   (0x7U << DMI_SBCS_SBACCESS_OFFSET)
+/*
+* When 1, {\tt sbaddress} is incremented by the access size (in
+* bytes) selected in \Fsbaccess after every system bus access.
+ */
+#define DMI_SBCS_SBAUTOINCREMENT_OFFSET     16
+#define DMI_SBCS_SBAUTOINCREMENT_LENGTH     1
+#define DMI_SBCS_SBAUTOINCREMENT            (0x1U << DMI_SBCS_SBAUTOINCREMENT_OFFSET)
+/*
+* When 1, every read from \Rsbdatazero automatically triggers a
+* system bus read at the (possibly auto-incremented) address.
+ */
+#define DMI_SBCS_SBREADONDATA_OFFSET        15
+#define DMI_SBCS_SBREADONDATA_LENGTH        1
+#define DMI_SBCS_SBREADONDATA               (0x1U << DMI_SBCS_SBREADONDATA_OFFSET)
+/*
+* When the Debug Module's system bus
+* master causes a bus error, this field gets set. The bits in this
+* field remain set until they are cleared by writing 1 to them.
+* While this field is non-zero, no more system bus accesses can be
+* initiated by the Debug Module.
+*
+* An implementation may report "Other" (7) for any error condition.
+*
+* 0: There was no bus error.
+*
+* 1: There was a timeout.
+*
+* 2: A bad address was accessed.
+*
+* 3: There was an alignment error.
+*
+* 4: An access of unsupported size was requested.
+*
+* 7: Other.
+ */
+#define DMI_SBCS_SBERROR_OFFSET             12
+#define DMI_SBCS_SBERROR_LENGTH             3
+#define DMI_SBCS_SBERROR                    (0x7U << DMI_SBCS_SBERROR_OFFSET)
+/*
+* Width of system bus addresses in bits. (0 indicates there is no bus
+* access support.)
+ */
+#define DMI_SBCS_SBASIZE_OFFSET             5
+#define DMI_SBCS_SBASIZE_LENGTH             7
+#define DMI_SBCS_SBASIZE                    (0x7fU << DMI_SBCS_SBASIZE_OFFSET)
+/*
+* 1 when 128-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS128_OFFSET         4
+#define DMI_SBCS_SBACCESS128_LENGTH         1
+#define DMI_SBCS_SBACCESS128                (0x1U << DMI_SBCS_SBACCESS128_OFFSET)
+/*
+* 1 when 64-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS64_OFFSET          3
+#define DMI_SBCS_SBACCESS64_LENGTH          1
+#define DMI_SBCS_SBACCESS64                 (0x1U << DMI_SBCS_SBACCESS64_OFFSET)
+/*
+* 1 when 32-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS32_OFFSET          2
+#define DMI_SBCS_SBACCESS32_LENGTH          1
+#define DMI_SBCS_SBACCESS32                 (0x1U << DMI_SBCS_SBACCESS32_OFFSET)
+/*
+* 1 when 16-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS16_OFFSET          1
+#define DMI_SBCS_SBACCESS16_LENGTH          1
+#define DMI_SBCS_SBACCESS16                 (0x1U << DMI_SBCS_SBACCESS16_OFFSET)
+/*
+* 1 when 8-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS8_OFFSET           0
+#define DMI_SBCS_SBACCESS8_LENGTH           1
+#define DMI_SBCS_SBACCESS8                  (0x1U << DMI_SBCS_SBACCESS8_OFFSET)
+#define DMI_SBADDRESS0                      0x39
+/*
+* Accesses bits 31:0 of the physical address in {\tt sbaddress}.
+ */
+#define DMI_SBADDRESS0_ADDRESS_OFFSET       0
+#define DMI_SBADDRESS0_ADDRESS_LENGTH       32
+#define DMI_SBADDRESS0_ADDRESS              (0xffffffffU << DMI_SBADDRESS0_ADDRESS_OFFSET)
+#define DMI_SBADDRESS1                      0x3a
+/*
+* Accesses bits 63:32 of the physical address in {\tt sbaddress} (if
+* the system address bus is that wide).
+ */
+#define DMI_SBADDRESS1_ADDRESS_OFFSET       0
+#define DMI_SBADDRESS1_ADDRESS_LENGTH       32
+#define DMI_SBADDRESS1_ADDRESS              (0xffffffffU << DMI_SBADDRESS1_ADDRESS_OFFSET)
+#define DMI_SBADDRESS2                      0x3b
+/*
+* Accesses bits 95:64 of the physical address in {\tt sbaddress} (if
+* the system address bus is that wide).
+ */
+#define DMI_SBADDRESS2_ADDRESS_OFFSET       0
+#define DMI_SBADDRESS2_ADDRESS_LENGTH       32
+#define DMI_SBADDRESS2_ADDRESS              (0xffffffffU << DMI_SBADDRESS2_ADDRESS_OFFSET)
+#define DMI_SBDATA0                         0x3c
+/*
+* Accesses bits 31:0 of {\tt sbdata}.
+ */
+#define DMI_SBDATA0_DATA_OFFSET             0
+#define DMI_SBDATA0_DATA_LENGTH             32
+#define DMI_SBDATA0_DATA                    (0xffffffffU << DMI_SBDATA0_DATA_OFFSET)
+#define DMI_SBDATA1                         0x3d
+/*
+* Accesses bits 63:32 of {\tt sbdata} (if the system bus is that
+* wide).
+ */
+#define DMI_SBDATA1_DATA_OFFSET             0
+#define DMI_SBDATA1_DATA_LENGTH             32
+#define DMI_SBDATA1_DATA                    (0xffffffffU << DMI_SBDATA1_DATA_OFFSET)
+#define DMI_SBDATA2                         0x3e
+/*
+* Accesses bits 95:64 of {\tt sbdata} (if the system bus is that
+* wide).
+ */
+#define DMI_SBDATA2_DATA_OFFSET             0
+#define DMI_SBDATA2_DATA_LENGTH             32
+#define DMI_SBDATA2_DATA                    (0xffffffffU << DMI_SBDATA2_DATA_OFFSET)
+#define DMI_SBDATA3                         0x3f
+/*
+* Accesses bits 127:96 of {\tt sbdata} (if the system bus is that
+* wide).
+ */
+#define DMI_SBDATA3_DATA_OFFSET             0
+#define DMI_SBDATA3_DATA_LENGTH             32
+#define DMI_SBDATA3_DATA                    (0xffffffffU << DMI_SBDATA3_DATA_OFFSET)
+#define SHORTNAME                           0x123
+/*
+* Description of what this field is used for.
+ */
+#define SHORTNAME_FIELD_OFFSET              0
+#define SHORTNAME_FIELD_LENGTH              8
+#define SHORTNAME_FIELD                     (0xffU << SHORTNAME_FIELD_OFFSET)
+#define AC_ACCESS_REGISTER                  None
+/*
+* This is 0 to indicate Access Register Command.
+ */
+#define AC_ACCESS_REGISTER_CMDTYPE_OFFSET   24
+#define AC_ACCESS_REGISTER_CMDTYPE_LENGTH   8
+#define AC_ACCESS_REGISTER_CMDTYPE          (0xffU << AC_ACCESS_REGISTER_CMDTYPE_OFFSET)
+/*
+* 2: Access the lowest 32 bits of the register.
+*
+* 3: Access the lowest 64 bits of the register.
+*
+* 4: Access the lowest 128 bits of the register.
+*
+* If \Fsize specifies a size larger than the register's actual size,
+* then the access must fail. If a register is accessible, then reads of \Fsize
+* less than or equal to the register's actual size must be supported.
+*
+* This field controls the Argument Width as referenced in
+* Table~\ref{tab:datareg}.
+ */
+#define AC_ACCESS_REGISTER_SIZE_OFFSET      20
+#define AC_ACCESS_REGISTER_SIZE_LENGTH      3
+#define AC_ACCESS_REGISTER_SIZE             (0x7U << AC_ACCESS_REGISTER_SIZE_OFFSET)
+/*
+* When 1, execute the program in the Program Buffer exactly once
+* after performing the transfer, if any.
+ */
+#define AC_ACCESS_REGISTER_POSTEXEC_OFFSET  18
+#define AC_ACCESS_REGISTER_POSTEXEC_LENGTH  1
+#define AC_ACCESS_REGISTER_POSTEXEC         (0x1U << AC_ACCESS_REGISTER_POSTEXEC_OFFSET)
+/*
+* 0: Don't do the operation specified by \Fwrite.
+*
+* 1: Do the operation specified by \Fwrite.
+*
+* This bit can be used to just execute the Program Buffer without
+* having to worry about placing valid values into \Fsize or \Fregno.
+ */
+#define AC_ACCESS_REGISTER_TRANSFER_OFFSET  17
+#define AC_ACCESS_REGISTER_TRANSFER_LENGTH  1
+#define AC_ACCESS_REGISTER_TRANSFER         (0x1U << AC_ACCESS_REGISTER_TRANSFER_OFFSET)
+/*
+* When \Ftransfer is set:
+* 0: Copy data from the specified register into {\tt arg0} portion
+* of {\tt data}.
+*
+* 1: Copy data from {\tt arg0} portion of {\tt data} into the
+* specified register.
+ */
+#define AC_ACCESS_REGISTER_WRITE_OFFSET     16
+#define AC_ACCESS_REGISTER_WRITE_LENGTH     1
+#define AC_ACCESS_REGISTER_WRITE            (0x1U << AC_ACCESS_REGISTER_WRITE_OFFSET)
+/*
+* Number of the register to access, as described in
+* Table~\ref{tab:regno}.
+* \Rdpc may be used as an alias for PC if this command is
+* supported on a non-halted hart.
+ */
+#define AC_ACCESS_REGISTER_REGNO_OFFSET     0
+#define AC_ACCESS_REGISTER_REGNO_LENGTH     16
+#define AC_ACCESS_REGISTER_REGNO            (0xffffU << AC_ACCESS_REGISTER_REGNO_OFFSET)
+#define AC_QUICK_ACCESS                     None
+/*
+* This is 1 to indicate Quick Access command.
+ */
+#define AC_QUICK_ACCESS_CMDTYPE_OFFSET      24
+#define AC_QUICK_ACCESS_CMDTYPE_LENGTH      8
+#define AC_QUICK_ACCESS_CMDTYPE             (0xffU << AC_QUICK_ACCESS_CMDTYPE_OFFSET)
+#define VIRT_PRIV                           virtual
+/*
+* Contains the privilege level the hart was operating in when Debug
+* Mode was entered. The encoding is described in Table
+* \ref{tab:privlevel}, and matches the privilege level encoding from
+* the RISC-V Privileged ISA Specification. A user can write this
+* value to change the hart's privilege level when exiting Debug Mode.
+ */
+#define VIRT_PRIV_PRV_OFFSET                0
+#define VIRT_PRIV_PRV_LENGTH                2
+#define VIRT_PRIV_PRV                       (0x3U << VIRT_PRIV_PRV_OFFSET)
diff --git a/src/target/riscv/encoding.h b/src/target/riscv/encoding.h
new file mode 100644 (file)
index 0000000..e214c0c
--- /dev/null
@@ -0,0 +1,1473 @@
+/* See LICENSE for license details. */
+
+#ifndef RISCV_CSR_ENCODING_H
+#define RISCV_CSR_ENCODING_H
+
+#define MSTATUS_UIE         0x00000001
+#define MSTATUS_SIE         0x00000002
+#define MSTATUS_HIE         0x00000004
+#define MSTATUS_MIE         0x00000008
+#define MSTATUS_UPIE        0x00000010
+#define MSTATUS_SPIE        0x00000020
+#define MSTATUS_HPIE        0x00000040
+#define MSTATUS_MPIE        0x00000080
+#define MSTATUS_SPP         0x00000100
+#define MSTATUS_HPP         0x00000600
+#define MSTATUS_MPP         0x00001800
+#define MSTATUS_FS          0x00006000
+#define MSTATUS_XS          0x00018000
+#define MSTATUS_MPRV        0x00020000
+#define MSTATUS_SUM         0x00040000
+#define MSTATUS_MXR         0x00080000
+#define MSTATUS_TVM         0x00100000
+#define MSTATUS_TW          0x00200000
+#define MSTATUS_TSR         0x00400000
+#define MSTATUS32_SD        0x80000000
+#define MSTATUS_UXL         0x0000000300000000
+#define MSTATUS_SXL         0x0000000C00000000
+#define MSTATUS64_SD        0x8000000000000000
+
+#define SSTATUS_UIE         0x00000001
+#define SSTATUS_SIE         0x00000002
+#define SSTATUS_UPIE        0x00000010
+#define SSTATUS_SPIE        0x00000020
+#define SSTATUS_SPP         0x00000100
+#define SSTATUS_FS          0x00006000
+#define SSTATUS_XS          0x00018000
+#define SSTATUS_SUM         0x00040000
+#define SSTATUS_MXR         0x00080000
+#define SSTATUS32_SD        0x80000000
+#define SSTATUS_UXL         0x0000000300000000
+#define SSTATUS64_SD        0x8000000000000000
+
+#define DCSR_XDEBUGVER      (3U<<30)
+#define DCSR_NDRESET        (1<<29)
+#define DCSR_FULLRESET      (1<<28)
+#define DCSR_EBREAKM        (1<<15)
+#define DCSR_EBREAKH        (1<<14)
+#define DCSR_EBREAKS        (1<<13)
+#define DCSR_EBREAKU        (1<<12)
+#define DCSR_STOPCYCLE      (1<<10)
+#define DCSR_STOPTIME       (1<<9)
+#define DCSR_CAUSE          (7<<6)
+#define DCSR_DEBUGINT       (1<<5)
+#define DCSR_HALT           (1<<3)
+#define DCSR_STEP           (1<<2)
+#define DCSR_PRV            (3<<0)
+
+#define DCSR_CAUSE_NONE     0
+#define DCSR_CAUSE_SWBP     1
+#define DCSR_CAUSE_HWBP     2
+#define DCSR_CAUSE_DEBUGINT 3
+#define DCSR_CAUSE_STEP     4
+#define DCSR_CAUSE_HALT     5
+
+#define MCONTROL_TYPE(xlen)    (0xfULL<<((xlen)-4))
+#define MCONTROL_DMODE(xlen)   (1ULL<<((xlen)-5))
+#define MCONTROL_MASKMAX(xlen) (0x3fULL<<((xlen)-11))
+
+#define MCONTROL_SELECT     (1<<19)
+#define MCONTROL_TIMING     (1<<18)
+#define MCONTROL_ACTION     (0x3f<<12)
+#define MCONTROL_CHAIN      (1<<11)
+#define MCONTROL_MATCH      (0xf<<7)
+#define MCONTROL_M          (1<<6)
+#define MCONTROL_H          (1<<5)
+#define MCONTROL_S          (1<<4)
+#define MCONTROL_U          (1<<3)
+#define MCONTROL_EXECUTE    (1<<2)
+#define MCONTROL_STORE      (1<<1)
+#define MCONTROL_LOAD       (1<<0)
+
+#define MCONTROL_TYPE_NONE      0
+#define MCONTROL_TYPE_MATCH     2
+
+#define MCONTROL_ACTION_DEBUG_EXCEPTION   0
+#define MCONTROL_ACTION_DEBUG_MODE        1
+#define MCONTROL_ACTION_TRACE_START       2
+#define MCONTROL_ACTION_TRACE_STOP        3
+#define MCONTROL_ACTION_TRACE_EMIT        4
+
+#define MCONTROL_MATCH_EQUAL     0
+#define MCONTROL_MATCH_NAPOT     1
+#define MCONTROL_MATCH_GE        2
+#define MCONTROL_MATCH_LT        3
+#define MCONTROL_MATCH_MASK_LOW  4
+#define MCONTROL_MATCH_MASK_HIGH 5
+
+#define MIP_SSIP            (1 << IRQ_S_SOFT)
+#define MIP_HSIP            (1 << IRQ_H_SOFT)
+#define MIP_MSIP            (1 << IRQ_M_SOFT)
+#define MIP_STIP            (1 << IRQ_S_TIMER)
+#define MIP_HTIP            (1 << IRQ_H_TIMER)
+#define MIP_MTIP            (1 << IRQ_M_TIMER)
+#define MIP_SEIP            (1 << IRQ_S_EXT)
+#define MIP_HEIP            (1 << IRQ_H_EXT)
+#define MIP_MEIP            (1 << IRQ_M_EXT)
+
+#define SIP_SSIP MIP_SSIP
+#define SIP_STIP MIP_STIP
+
+#define PRV_U 0
+#define PRV_S 1
+#define PRV_H 2
+#define PRV_M 3
+
+#define SATP32_MODE 0x80000000
+#define SATP32_ASID 0x7FC00000
+#define SATP32_PPN  0x003FFFFF
+#define SATP64_MODE 0xF000000000000000
+#define SATP64_ASID 0x0FFFF00000000000
+#define SATP64_PPN  0x00000FFFFFFFFFFF
+
+#define SATP_MODE_OFF  0
+#define SATP_MODE_SV32 1
+#define SATP_MODE_SV39 8
+#define SATP_MODE_SV48 9
+#define SATP_MODE_SV57 10
+#define SATP_MODE_SV64 11
+
+#define PMP_R     0x01
+#define PMP_W     0x02
+#define PMP_X     0x04
+#define PMP_A     0x18
+#define PMP_L     0x80
+#define PMP_SHIFT 2
+
+#define PMP_TOR   0x08
+#define PMP_NA4   0x10
+#define PMP_NAPOT 0x18
+
+#define IRQ_S_SOFT   1
+#define IRQ_H_SOFT   2
+#define IRQ_M_SOFT   3
+#define IRQ_S_TIMER  5
+#define IRQ_H_TIMER  6
+#define IRQ_M_TIMER  7
+#define IRQ_S_EXT    9
+#define IRQ_H_EXT    10
+#define IRQ_M_EXT    11
+#define IRQ_COP      12
+#define IRQ_HOST     13
+
+#define DEFAULT_RSTVEC     0x00001000
+#define CLINT_BASE         0x02000000
+#define CLINT_SIZE         0x000c0000
+#define EXT_IO_BASE        0x40000000
+#define DRAM_BASE          0x80000000
+
+/* page table entry (PTE) fields */
+#define PTE_V     0x001 /* Valid */
+#define PTE_R     0x002 /* Read */
+#define PTE_W     0x004 /* Write */
+#define PTE_X     0x008 /* Execute */
+#define PTE_U     0x010 /* User */
+#define PTE_G     0x020 /* Global */
+#define PTE_A     0x040 /* Accessed */
+#define PTE_D     0x080 /* Dirty */
+#define PTE_SOFT  0x300 /* Reserved for Software */
+
+#define PTE_PPN_SHIFT 10
+
+#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V)
+
+#ifdef __riscv
+
+#if __riscv_xlen == 64
+# define MSTATUS_SD MSTATUS64_SD
+# define SSTATUS_SD SSTATUS64_SD
+# define RISCV_PGLEVEL_BITS 9
+# define SATP_MODE SATP64_MODE
+#else
+# define MSTATUS_SD MSTATUS32_SD
+# define SSTATUS_SD SSTATUS32_SD
+# define RISCV_PGLEVEL_BITS 10
+# define SATP_MODE SATP32_MODE
+#endif
+#define RISCV_PGSHIFT 12
+#define RISCV_PGSIZE (1 << RISCV_PGSHIFT)
+
+#ifndef __ASSEMBLER__
+
+#ifdef __GNUC__
+
+/*
+#define read_csr(reg) ({ unsigned long __tmp; \
+  asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \
+  __tmp; })
+
+#define write_csr(reg, val) ({ \
+  asm volatile ("csrw " #reg ", %0" :: "rK"(val)); })
+
+#define swap_csr(reg, val) ({ unsigned long __tmp; \
+  asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "rK"(val)); \
+  __tmp; })
+
+#define set_csr(reg, bit) ({ unsigned long __tmp; \
+  asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \
+  __tmp; })
+
+#define clear_csr(reg, bit) ({ unsigned long __tmp; \
+  asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \
+  __tmp; })
+  */
+
+#define rdtime() read_csr(time)
+#define rdcycle() read_csr(cycle)
+#define rdinstret() read_csr(instret)
+
+#endif
+
+#endif
+
+#endif
+
+#endif
+/* Automatically generated by parse-opcodes.  */
+#ifndef RISCV_ENCODING_H
+#define RISCV_ENCODING_H
+#define MATCH_BEQ 0x63
+#define MASK_BEQ  0x707f
+#define MATCH_BNE 0x1063
+#define MASK_BNE  0x707f
+#define MATCH_BLT 0x4063
+#define MASK_BLT  0x707f
+#define MATCH_BGE 0x5063
+#define MASK_BGE  0x707f
+#define MATCH_BLTU 0x6063
+#define MASK_BLTU  0x707f
+#define MATCH_BGEU 0x7063
+#define MASK_BGEU  0x707f
+#define MATCH_JALR 0x67
+#define MASK_JALR  0x707f
+#define MATCH_JAL 0x6f
+#define MASK_JAL  0x7f
+#define MATCH_LUI 0x37
+#define MASK_LUI  0x7f
+#define MATCH_AUIPC 0x17
+#define MASK_AUIPC  0x7f
+#define MATCH_ADDI 0x13
+#define MASK_ADDI  0x707f
+#define MATCH_SLLI 0x1013
+#define MASK_SLLI  0xfc00707f
+#define MATCH_SLTI 0x2013
+#define MASK_SLTI  0x707f
+#define MATCH_SLTIU 0x3013
+#define MASK_SLTIU  0x707f
+#define MATCH_XORI 0x4013
+#define MASK_XORI  0x707f
+#define MATCH_SRLI 0x5013
+#define MASK_SRLI  0xfc00707f
+#define MATCH_SRAI 0x40005013
+#define MASK_SRAI  0xfc00707f
+#define MATCH_ORI 0x6013
+#define MASK_ORI  0x707f
+#define MATCH_ANDI 0x7013
+#define MASK_ANDI  0x707f
+#define MATCH_ADD 0x33
+#define MASK_ADD  0xfe00707f
+#define MATCH_SUB 0x40000033
+#define MASK_SUB  0xfe00707f
+#define MATCH_SLL 0x1033
+#define MASK_SLL  0xfe00707f
+#define MATCH_SLT 0x2033
+#define MASK_SLT  0xfe00707f
+#define MATCH_SLTU 0x3033
+#define MASK_SLTU  0xfe00707f
+#define MATCH_XOR 0x4033
+#define MASK_XOR  0xfe00707f
+#define MATCH_SRL 0x5033
+#define MASK_SRL  0xfe00707f
+#define MATCH_SRA 0x40005033
+#define MASK_SRA  0xfe00707f
+#define MATCH_OR 0x6033
+#define MASK_OR  0xfe00707f
+#define MATCH_AND 0x7033
+#define MASK_AND  0xfe00707f
+#define MATCH_ADDIW 0x1b
+#define MASK_ADDIW  0x707f
+#define MATCH_SLLIW 0x101b
+#define MASK_SLLIW  0xfe00707f
+#define MATCH_SRLIW 0x501b
+#define MASK_SRLIW  0xfe00707f
+#define MATCH_SRAIW 0x4000501b
+#define MASK_SRAIW  0xfe00707f
+#define MATCH_ADDW 0x3b
+#define MASK_ADDW  0xfe00707f
+#define MATCH_SUBW 0x4000003b
+#define MASK_SUBW  0xfe00707f
+#define MATCH_SLLW 0x103b
+#define MASK_SLLW  0xfe00707f
+#define MATCH_SRLW 0x503b
+#define MASK_SRLW  0xfe00707f
+#define MATCH_SRAW 0x4000503b
+#define MASK_SRAW  0xfe00707f
+#define MATCH_LB 0x3
+#define MASK_LB  0x707f
+#define MATCH_LH 0x1003
+#define MASK_LH  0x707f
+#define MATCH_LW 0x2003
+#define MASK_LW  0x707f
+#define MATCH_LD 0x3003
+#define MASK_LD  0x707f
+#define MATCH_LBU 0x4003
+#define MASK_LBU  0x707f
+#define MATCH_LHU 0x5003
+#define MASK_LHU  0x707f
+#define MATCH_LWU 0x6003
+#define MASK_LWU  0x707f
+#define MATCH_SB 0x23
+#define MASK_SB  0x707f
+#define MATCH_SH 0x1023
+#define MASK_SH  0x707f
+#define MATCH_SW 0x2023
+#define MASK_SW  0x707f
+#define MATCH_SD 0x3023
+#define MASK_SD  0x707f
+#define MATCH_FENCE 0xf
+#define MASK_FENCE  0x707f
+#define MATCH_FENCE_I 0x100f
+#define MASK_FENCE_I  0x707f
+#define MATCH_MUL 0x2000033
+#define MASK_MUL  0xfe00707f
+#define MATCH_MULH 0x2001033
+#define MASK_MULH  0xfe00707f
+#define MATCH_MULHSU 0x2002033
+#define MASK_MULHSU  0xfe00707f
+#define MATCH_MULHU 0x2003033
+#define MASK_MULHU  0xfe00707f
+#define MATCH_DIV 0x2004033
+#define MASK_DIV  0xfe00707f
+#define MATCH_DIVU 0x2005033
+#define MASK_DIVU  0xfe00707f
+#define MATCH_REM 0x2006033
+#define MASK_REM  0xfe00707f
+#define MATCH_REMU 0x2007033
+#define MASK_REMU  0xfe00707f
+#define MATCH_MULW 0x200003b
+#define MASK_MULW  0xfe00707f
+#define MATCH_DIVW 0x200403b
+#define MASK_DIVW  0xfe00707f
+#define MATCH_DIVUW 0x200503b
+#define MASK_DIVUW  0xfe00707f
+#define MATCH_REMW 0x200603b
+#define MASK_REMW  0xfe00707f
+#define MATCH_REMUW 0x200703b
+#define MASK_REMUW  0xfe00707f
+#define MATCH_AMOADD_W 0x202f
+#define MASK_AMOADD_W  0xf800707f
+#define MATCH_AMOXOR_W 0x2000202f
+#define MASK_AMOXOR_W  0xf800707f
+#define MATCH_AMOOR_W 0x4000202f
+#define MASK_AMOOR_W  0xf800707f
+#define MATCH_AMOAND_W 0x6000202f
+#define MASK_AMOAND_W  0xf800707f
+#define MATCH_AMOMIN_W 0x8000202f
+#define MASK_AMOMIN_W  0xf800707f
+#define MATCH_AMOMAX_W 0xa000202f
+#define MASK_AMOMAX_W  0xf800707f
+#define MATCH_AMOMINU_W 0xc000202f
+#define MASK_AMOMINU_W  0xf800707f
+#define MATCH_AMOMAXU_W 0xe000202f
+#define MASK_AMOMAXU_W  0xf800707f
+#define MATCH_AMOSWAP_W 0x800202f
+#define MASK_AMOSWAP_W  0xf800707f
+#define MATCH_LR_W 0x1000202f
+#define MASK_LR_W  0xf9f0707f
+#define MATCH_SC_W 0x1800202f
+#define MASK_SC_W  0xf800707f
+#define MATCH_AMOADD_D 0x302f
+#define MASK_AMOADD_D  0xf800707f
+#define MATCH_AMOXOR_D 0x2000302f
+#define MASK_AMOXOR_D  0xf800707f
+#define MATCH_AMOOR_D 0x4000302f
+#define MASK_AMOOR_D  0xf800707f
+#define MATCH_AMOAND_D 0x6000302f
+#define MASK_AMOAND_D  0xf800707f
+#define MATCH_AMOMIN_D 0x8000302f
+#define MASK_AMOMIN_D  0xf800707f
+#define MATCH_AMOMAX_D 0xa000302f
+#define MASK_AMOMAX_D  0xf800707f
+#define MATCH_AMOMINU_D 0xc000302f
+#define MASK_AMOMINU_D  0xf800707f
+#define MATCH_AMOMAXU_D 0xe000302f
+#define MASK_AMOMAXU_D  0xf800707f
+#define MATCH_AMOSWAP_D 0x800302f
+#define MASK_AMOSWAP_D  0xf800707f
+#define MATCH_LR_D 0x1000302f
+#define MASK_LR_D  0xf9f0707f
+#define MATCH_SC_D 0x1800302f
+#define MASK_SC_D  0xf800707f
+#define MATCH_ECALL 0x73
+#define MASK_ECALL  0xffffffff
+#define MATCH_EBREAK 0x100073
+#define MASK_EBREAK  0xffffffff
+#define MATCH_URET 0x200073
+#define MASK_URET  0xffffffff
+#define MATCH_SRET 0x10200073
+#define MASK_SRET  0xffffffff
+#define MATCH_MRET 0x30200073
+#define MASK_MRET  0xffffffff
+#define MATCH_DRET 0x7b200073
+#define MASK_DRET  0xffffffff
+#define MATCH_SFENCE_VMA 0x12000073
+#define MASK_SFENCE_VMA  0xfe007fff
+#define MATCH_WFI 0x10500073
+#define MASK_WFI  0xffffffff
+#define MATCH_CSRRW 0x1073
+#define MASK_CSRRW  0x707f
+#define MATCH_CSRRS 0x2073
+#define MASK_CSRRS  0x707f
+#define MATCH_CSRRC 0x3073
+#define MASK_CSRRC  0x707f
+#define MATCH_CSRRWI 0x5073
+#define MASK_CSRRWI  0x707f
+#define MATCH_CSRRSI 0x6073
+#define MASK_CSRRSI  0x707f
+#define MATCH_CSRRCI 0x7073
+#define MASK_CSRRCI  0x707f
+#define MATCH_FADD_S 0x53
+#define MASK_FADD_S  0xfe00007f
+#define MATCH_FSUB_S 0x8000053
+#define MASK_FSUB_S  0xfe00007f
+#define MATCH_FMUL_S 0x10000053
+#define MASK_FMUL_S  0xfe00007f
+#define MATCH_FDIV_S 0x18000053
+#define MASK_FDIV_S  0xfe00007f
+#define MATCH_FSGNJ_S 0x20000053
+#define MASK_FSGNJ_S  0xfe00707f
+#define MATCH_FSGNJN_S 0x20001053
+#define MASK_FSGNJN_S  0xfe00707f
+#define MATCH_FSGNJX_S 0x20002053
+#define MASK_FSGNJX_S  0xfe00707f
+#define MATCH_FMIN_S 0x28000053
+#define MASK_FMIN_S  0xfe00707f
+#define MATCH_FMAX_S 0x28001053
+#define MASK_FMAX_S  0xfe00707f
+#define MATCH_FSQRT_S 0x58000053
+#define MASK_FSQRT_S  0xfff0007f
+#define MATCH_FADD_D 0x2000053
+#define MASK_FADD_D  0xfe00007f
+#define MATCH_FSUB_D 0xa000053
+#define MASK_FSUB_D  0xfe00007f
+#define MATCH_FMUL_D 0x12000053
+#define MASK_FMUL_D  0xfe00007f
+#define MATCH_FDIV_D 0x1a000053
+#define MASK_FDIV_D  0xfe00007f
+#define MATCH_FSGNJ_D 0x22000053
+#define MASK_FSGNJ_D  0xfe00707f
+#define MATCH_FSGNJN_D 0x22001053
+#define MASK_FSGNJN_D  0xfe00707f
+#define MATCH_FSGNJX_D 0x22002053
+#define MASK_FSGNJX_D  0xfe00707f
+#define MATCH_FMIN_D 0x2a000053
+#define MASK_FMIN_D  0xfe00707f
+#define MATCH_FMAX_D 0x2a001053
+#define MASK_FMAX_D  0xfe00707f
+#define MATCH_FCVT_S_D 0x40100053
+#define MASK_FCVT_S_D  0xfff0007f
+#define MATCH_FCVT_D_S 0x42000053
+#define MASK_FCVT_D_S  0xfff0007f
+#define MATCH_FSQRT_D 0x5a000053
+#define MASK_FSQRT_D  0xfff0007f
+#define MATCH_FADD_Q 0x6000053
+#define MASK_FADD_Q  0xfe00007f
+#define MATCH_FSUB_Q 0xe000053
+#define MASK_FSUB_Q  0xfe00007f
+#define MATCH_FMUL_Q 0x16000053
+#define MASK_FMUL_Q  0xfe00007f
+#define MATCH_FDIV_Q 0x1e000053
+#define MASK_FDIV_Q  0xfe00007f
+#define MATCH_FSGNJ_Q 0x26000053
+#define MASK_FSGNJ_Q  0xfe00707f
+#define MATCH_FSGNJN_Q 0x26001053
+#define MASK_FSGNJN_Q  0xfe00707f
+#define MATCH_FSGNJX_Q 0x26002053
+#define MASK_FSGNJX_Q  0xfe00707f
+#define MATCH_FMIN_Q 0x2e000053
+#define MASK_FMIN_Q  0xfe00707f
+#define MATCH_FMAX_Q 0x2e001053
+#define MASK_FMAX_Q  0xfe00707f
+#define MATCH_FCVT_S_Q 0x40300053
+#define MASK_FCVT_S_Q  0xfff0007f
+#define MATCH_FCVT_Q_S 0x46000053
+#define MASK_FCVT_Q_S  0xfff0007f
+#define MATCH_FCVT_D_Q 0x42300053
+#define MASK_FCVT_D_Q  0xfff0007f
+#define MATCH_FCVT_Q_D 0x46100053
+#define MASK_FCVT_Q_D  0xfff0007f
+#define MATCH_FSQRT_Q 0x5e000053
+#define MASK_FSQRT_Q  0xfff0007f
+#define MATCH_FLE_S 0xa0000053
+#define MASK_FLE_S  0xfe00707f
+#define MATCH_FLT_S 0xa0001053
+#define MASK_FLT_S  0xfe00707f
+#define MATCH_FEQ_S 0xa0002053
+#define MASK_FEQ_S  0xfe00707f
+#define MATCH_FLE_D 0xa2000053
+#define MASK_FLE_D  0xfe00707f
+#define MATCH_FLT_D 0xa2001053
+#define MASK_FLT_D  0xfe00707f
+#define MATCH_FEQ_D 0xa2002053
+#define MASK_FEQ_D  0xfe00707f
+#define MATCH_FLE_Q 0xa6000053
+#define MASK_FLE_Q  0xfe00707f
+#define MATCH_FLT_Q 0xa6001053
+#define MASK_FLT_Q  0xfe00707f
+#define MATCH_FEQ_Q 0xa6002053
+#define MASK_FEQ_Q  0xfe00707f
+#define MATCH_FCVT_W_S 0xc0000053
+#define MASK_FCVT_W_S  0xfff0007f
+#define MATCH_FCVT_WU_S 0xc0100053
+#define MASK_FCVT_WU_S  0xfff0007f
+#define MATCH_FCVT_L_S 0xc0200053
+#define MASK_FCVT_L_S  0xfff0007f
+#define MATCH_FCVT_LU_S 0xc0300053
+#define MASK_FCVT_LU_S  0xfff0007f
+#define MATCH_FMV_X_W 0xe0000053
+#define MASK_FMV_X_W  0xfff0707f
+#define MATCH_FCLASS_S 0xe0001053
+#define MASK_FCLASS_S  0xfff0707f
+#define MATCH_FCVT_W_D 0xc2000053
+#define MASK_FCVT_W_D  0xfff0007f
+#define MATCH_FCVT_WU_D 0xc2100053
+#define MASK_FCVT_WU_D  0xfff0007f
+#define MATCH_FCVT_L_D 0xc2200053
+#define MASK_FCVT_L_D  0xfff0007f
+#define MATCH_FCVT_LU_D 0xc2300053
+#define MASK_FCVT_LU_D  0xfff0007f
+#define MATCH_FMV_X_D 0xe2000053
+#define MASK_FMV_X_D  0xfff0707f
+#define MATCH_FCLASS_D 0xe2001053
+#define MASK_FCLASS_D  0xfff0707f
+#define MATCH_FCVT_W_Q 0xc6000053
+#define MASK_FCVT_W_Q  0xfff0007f
+#define MATCH_FCVT_WU_Q 0xc6100053
+#define MASK_FCVT_WU_Q  0xfff0007f
+#define MATCH_FCVT_L_Q 0xc6200053
+#define MASK_FCVT_L_Q  0xfff0007f
+#define MATCH_FCVT_LU_Q 0xc6300053
+#define MASK_FCVT_LU_Q  0xfff0007f
+#define MATCH_FMV_X_Q 0xe6000053
+#define MASK_FMV_X_Q  0xfff0707f
+#define MATCH_FCLASS_Q 0xe6001053
+#define MASK_FCLASS_Q  0xfff0707f
+#define MATCH_FCVT_S_W 0xd0000053
+#define MASK_FCVT_S_W  0xfff0007f
+#define MATCH_FCVT_S_WU 0xd0100053
+#define MASK_FCVT_S_WU  0xfff0007f
+#define MATCH_FCVT_S_L 0xd0200053
+#define MASK_FCVT_S_L  0xfff0007f
+#define MATCH_FCVT_S_LU 0xd0300053
+#define MASK_FCVT_S_LU  0xfff0007f
+#define MATCH_FMV_W_X 0xf0000053
+#define MASK_FMV_W_X  0xfff0707f
+#define MATCH_FCVT_D_W 0xd2000053
+#define MASK_FCVT_D_W  0xfff0007f
+#define MATCH_FCVT_D_WU 0xd2100053
+#define MASK_FCVT_D_WU  0xfff0007f
+#define MATCH_FCVT_D_L 0xd2200053
+#define MASK_FCVT_D_L  0xfff0007f
+#define MATCH_FCVT_D_LU 0xd2300053
+#define MASK_FCVT_D_LU  0xfff0007f
+#define MATCH_FMV_D_X 0xf2000053
+#define MASK_FMV_D_X  0xfff0707f
+#define MATCH_FCVT_Q_W 0xd6000053
+#define MASK_FCVT_Q_W  0xfff0007f
+#define MATCH_FCVT_Q_WU 0xd6100053
+#define MASK_FCVT_Q_WU  0xfff0007f
+#define MATCH_FCVT_Q_L 0xd6200053
+#define MASK_FCVT_Q_L  0xfff0007f
+#define MATCH_FCVT_Q_LU 0xd6300053
+#define MASK_FCVT_Q_LU  0xfff0007f
+#define MATCH_FMV_Q_X 0xf6000053
+#define MASK_FMV_Q_X  0xfff0707f
+#define MATCH_FLW 0x2007
+#define MASK_FLW  0x707f
+#define MATCH_FLD 0x3007
+#define MASK_FLD  0x707f
+#define MATCH_FLQ 0x4007
+#define MASK_FLQ  0x707f
+#define MATCH_FSW 0x2027
+#define MASK_FSW  0x707f
+#define MATCH_FSD 0x3027
+#define MASK_FSD  0x707f
+#define MATCH_FSQ 0x4027
+#define MASK_FSQ  0x707f
+#define MATCH_FMADD_S 0x43
+#define MASK_FMADD_S  0x600007f
+#define MATCH_FMSUB_S 0x47
+#define MASK_FMSUB_S  0x600007f
+#define MATCH_FNMSUB_S 0x4b
+#define MASK_FNMSUB_S  0x600007f
+#define MATCH_FNMADD_S 0x4f
+#define MASK_FNMADD_S  0x600007f
+#define MATCH_FMADD_D 0x2000043
+#define MASK_FMADD_D  0x600007f
+#define MATCH_FMSUB_D 0x2000047
+#define MASK_FMSUB_D  0x600007f
+#define MATCH_FNMSUB_D 0x200004b
+#define MASK_FNMSUB_D  0x600007f
+#define MATCH_FNMADD_D 0x200004f
+#define MASK_FNMADD_D  0x600007f
+#define MATCH_FMADD_Q 0x6000043
+#define MASK_FMADD_Q  0x600007f
+#define MATCH_FMSUB_Q 0x6000047
+#define MASK_FMSUB_Q  0x600007f
+#define MATCH_FNMSUB_Q 0x600004b
+#define MASK_FNMSUB_Q  0x600007f
+#define MATCH_FNMADD_Q 0x600004f
+#define MASK_FNMADD_Q  0x600007f
+#define MATCH_C_NOP 0x1
+#define MASK_C_NOP  0xffff
+#define MATCH_C_ADDI16SP 0x6101
+#define MASK_C_ADDI16SP  0xef83
+#define MATCH_C_JR 0x8002
+#define MASK_C_JR  0xf07f
+#define MATCH_C_JALR 0x9002
+#define MASK_C_JALR  0xf07f
+#define MATCH_C_EBREAK 0x9002
+#define MASK_C_EBREAK  0xffff
+#define MATCH_C_LD 0x6000
+#define MASK_C_LD  0xe003
+#define MATCH_C_SD 0xe000
+#define MASK_C_SD  0xe003
+#define MATCH_C_ADDIW 0x2001
+#define MASK_C_ADDIW  0xe003
+#define MATCH_C_LDSP 0x6002
+#define MASK_C_LDSP  0xe003
+#define MATCH_C_SDSP 0xe002
+#define MASK_C_SDSP  0xe003
+#define MATCH_C_ADDI4SPN 0x0
+#define MASK_C_ADDI4SPN  0xe003
+#define MATCH_C_FLD 0x2000
+#define MASK_C_FLD  0xe003
+#define MATCH_C_LW 0x4000
+#define MASK_C_LW  0xe003
+#define MATCH_C_FLW 0x6000
+#define MASK_C_FLW  0xe003
+#define MATCH_C_FSD 0xa000
+#define MASK_C_FSD  0xe003
+#define MATCH_C_SW 0xc000
+#define MASK_C_SW  0xe003
+#define MATCH_C_FSW 0xe000
+#define MASK_C_FSW  0xe003
+#define MATCH_C_ADDI 0x1
+#define MASK_C_ADDI  0xe003
+#define MATCH_C_JAL 0x2001
+#define MASK_C_JAL  0xe003
+#define MATCH_C_LI 0x4001
+#define MASK_C_LI  0xe003
+#define MATCH_C_LUI 0x6001
+#define MASK_C_LUI  0xe003
+#define MATCH_C_SRLI 0x8001
+#define MASK_C_SRLI  0xec03
+#define MATCH_C_SRAI 0x8401
+#define MASK_C_SRAI  0xec03
+#define MATCH_C_ANDI 0x8801
+#define MASK_C_ANDI  0xec03
+#define MATCH_C_SUB 0x8c01
+#define MASK_C_SUB  0xfc63
+#define MATCH_C_XOR 0x8c21
+#define MASK_C_XOR  0xfc63
+#define MATCH_C_OR 0x8c41
+#define MASK_C_OR  0xfc63
+#define MATCH_C_AND 0x8c61
+#define MASK_C_AND  0xfc63
+#define MATCH_C_SUBW 0x9c01
+#define MASK_C_SUBW  0xfc63
+#define MATCH_C_ADDW 0x9c21
+#define MASK_C_ADDW  0xfc63
+#define MATCH_C_J 0xa001
+#define MASK_C_J  0xe003
+#define MATCH_C_BEQZ 0xc001
+#define MASK_C_BEQZ  0xe003
+#define MATCH_C_BNEZ 0xe001
+#define MASK_C_BNEZ  0xe003
+#define MATCH_C_SLLI 0x2
+#define MASK_C_SLLI  0xe003
+#define MATCH_C_FLDSP 0x2002
+#define MASK_C_FLDSP  0xe003
+#define MATCH_C_LWSP 0x4002
+#define MASK_C_LWSP  0xe003
+#define MATCH_C_FLWSP 0x6002
+#define MASK_C_FLWSP  0xe003
+#define MATCH_C_MV 0x8002
+#define MASK_C_MV  0xf003
+#define MATCH_C_ADD 0x9002
+#define MASK_C_ADD  0xf003
+#define MATCH_C_FSDSP 0xa002
+#define MASK_C_FSDSP  0xe003
+#define MATCH_C_SWSP 0xc002
+#define MASK_C_SWSP  0xe003
+#define MATCH_C_FSWSP 0xe002
+#define MASK_C_FSWSP  0xe003
+#define MATCH_CUSTOM0 0xb
+#define MASK_CUSTOM0  0x707f
+#define MATCH_CUSTOM0_RS1 0x200b
+#define MASK_CUSTOM0_RS1  0x707f
+#define MATCH_CUSTOM0_RS1_RS2 0x300b
+#define MASK_CUSTOM0_RS1_RS2  0x707f
+#define MATCH_CUSTOM0_RD 0x400b
+#define MASK_CUSTOM0_RD  0x707f
+#define MATCH_CUSTOM0_RD_RS1 0x600b
+#define MASK_CUSTOM0_RD_RS1  0x707f
+#define MATCH_CUSTOM0_RD_RS1_RS2 0x700b
+#define MASK_CUSTOM0_RD_RS1_RS2  0x707f
+#define MATCH_CUSTOM1 0x2b
+#define MASK_CUSTOM1  0x707f
+#define MATCH_CUSTOM1_RS1 0x202b
+#define MASK_CUSTOM1_RS1  0x707f
+#define MATCH_CUSTOM1_RS1_RS2 0x302b
+#define MASK_CUSTOM1_RS1_RS2  0x707f
+#define MATCH_CUSTOM1_RD 0x402b
+#define MASK_CUSTOM1_RD  0x707f
+#define MATCH_CUSTOM1_RD_RS1 0x602b
+#define MASK_CUSTOM1_RD_RS1  0x707f
+#define MATCH_CUSTOM1_RD_RS1_RS2 0x702b
+#define MASK_CUSTOM1_RD_RS1_RS2  0x707f
+#define MATCH_CUSTOM2 0x5b
+#define MASK_CUSTOM2  0x707f
+#define MATCH_CUSTOM2_RS1 0x205b
+#define MASK_CUSTOM2_RS1  0x707f
+#define MATCH_CUSTOM2_RS1_RS2 0x305b
+#define MASK_CUSTOM2_RS1_RS2  0x707f
+#define MATCH_CUSTOM2_RD 0x405b
+#define MASK_CUSTOM2_RD  0x707f
+#define MATCH_CUSTOM2_RD_RS1 0x605b
+#define MASK_CUSTOM2_RD_RS1  0x707f
+#define MATCH_CUSTOM2_RD_RS1_RS2 0x705b
+#define MASK_CUSTOM2_RD_RS1_RS2  0x707f
+#define MATCH_CUSTOM3 0x7b
+#define MASK_CUSTOM3  0x707f
+#define MATCH_CUSTOM3_RS1 0x207b
+#define MASK_CUSTOM3_RS1  0x707f
+#define MATCH_CUSTOM3_RS1_RS2 0x307b
+#define MASK_CUSTOM3_RS1_RS2  0x707f
+#define MATCH_CUSTOM3_RD 0x407b
+#define MASK_CUSTOM3_RD  0x707f
+#define MATCH_CUSTOM3_RD_RS1 0x607b
+#define MASK_CUSTOM3_RD_RS1  0x707f
+#define MATCH_CUSTOM3_RD_RS1_RS2 0x707b
+#define MASK_CUSTOM3_RD_RS1_RS2  0x707f
+#define CSR_FFLAGS 0x1
+#define CSR_FRM 0x2
+#define CSR_FCSR 0x3
+#define CSR_CYCLE 0xc00
+#define CSR_TIME 0xc01
+#define CSR_INSTRET 0xc02
+#define CSR_HPMCOUNTER3 0xc03
+#define CSR_HPMCOUNTER4 0xc04
+#define CSR_HPMCOUNTER5 0xc05
+#define CSR_HPMCOUNTER6 0xc06
+#define CSR_HPMCOUNTER7 0xc07
+#define CSR_HPMCOUNTER8 0xc08
+#define CSR_HPMCOUNTER9 0xc09
+#define CSR_HPMCOUNTER10 0xc0a
+#define CSR_HPMCOUNTER11 0xc0b
+#define CSR_HPMCOUNTER12 0xc0c
+#define CSR_HPMCOUNTER13 0xc0d
+#define CSR_HPMCOUNTER14 0xc0e
+#define CSR_HPMCOUNTER15 0xc0f
+#define CSR_HPMCOUNTER16 0xc10
+#define CSR_HPMCOUNTER17 0xc11
+#define CSR_HPMCOUNTER18 0xc12
+#define CSR_HPMCOUNTER19 0xc13
+#define CSR_HPMCOUNTER20 0xc14
+#define CSR_HPMCOUNTER21 0xc15
+#define CSR_HPMCOUNTER22 0xc16
+#define CSR_HPMCOUNTER23 0xc17
+#define CSR_HPMCOUNTER24 0xc18
+#define CSR_HPMCOUNTER25 0xc19
+#define CSR_HPMCOUNTER26 0xc1a
+#define CSR_HPMCOUNTER27 0xc1b
+#define CSR_HPMCOUNTER28 0xc1c
+#define CSR_HPMCOUNTER29 0xc1d
+#define CSR_HPMCOUNTER30 0xc1e
+#define CSR_HPMCOUNTER31 0xc1f
+#define CSR_SSTATUS 0x100
+#define CSR_SIE 0x104
+#define CSR_STVEC 0x105
+#define CSR_SCOUNTEREN 0x106
+#define CSR_SSCRATCH 0x140
+#define CSR_SEPC 0x141
+#define CSR_SCAUSE 0x142
+#define CSR_STVAL 0x143
+#define CSR_SIP 0x144
+#define CSR_SATP 0x180
+#define CSR_MSTATUS 0x300
+#define CSR_MISA 0x301
+#define CSR_MEDELEG 0x302
+#define CSR_MIDELEG 0x303
+#define CSR_MIE 0x304
+#define CSR_MTVEC 0x305
+#define CSR_MCOUNTEREN 0x306
+#define CSR_MSCRATCH 0x340
+#define CSR_MEPC 0x341
+#define CSR_MCAUSE 0x342
+#define CSR_MTVAL 0x343
+#define CSR_MIP 0x344
+#define CSR_PMPCFG0 0x3a0
+#define CSR_PMPCFG1 0x3a1
+#define CSR_PMPCFG2 0x3a2
+#define CSR_PMPCFG3 0x3a3
+#define CSR_PMPADDR0 0x3b0
+#define CSR_PMPADDR1 0x3b1
+#define CSR_PMPADDR2 0x3b2
+#define CSR_PMPADDR3 0x3b3
+#define CSR_PMPADDR4 0x3b4
+#define CSR_PMPADDR5 0x3b5
+#define CSR_PMPADDR6 0x3b6
+#define CSR_PMPADDR7 0x3b7
+#define CSR_PMPADDR8 0x3b8
+#define CSR_PMPADDR9 0x3b9
+#define CSR_PMPADDR10 0x3ba
+#define CSR_PMPADDR11 0x3bb
+#define CSR_PMPADDR12 0x3bc
+#define CSR_PMPADDR13 0x3bd
+#define CSR_PMPADDR14 0x3be
+#define CSR_PMPADDR15 0x3bf
+#define CSR_TSELECT 0x7a0
+#define CSR_TDATA1 0x7a1
+#define CSR_TDATA2 0x7a2
+#define CSR_TDATA3 0x7a3
+#define CSR_DCSR 0x7b0
+#define CSR_DPC 0x7b1
+#define CSR_DSCRATCH 0x7b2
+#define CSR_MCYCLE 0xb00
+#define CSR_MINSTRET 0xb02
+#define CSR_MHPMCOUNTER3 0xb03
+#define CSR_MHPMCOUNTER4 0xb04
+#define CSR_MHPMCOUNTER5 0xb05
+#define CSR_MHPMCOUNTER6 0xb06
+#define CSR_MHPMCOUNTER7 0xb07
+#define CSR_MHPMCOUNTER8 0xb08
+#define CSR_MHPMCOUNTER9 0xb09
+#define CSR_MHPMCOUNTER10 0xb0a
+#define CSR_MHPMCOUNTER11 0xb0b
+#define CSR_MHPMCOUNTER12 0xb0c
+#define CSR_MHPMCOUNTER13 0xb0d
+#define CSR_MHPMCOUNTER14 0xb0e
+#define CSR_MHPMCOUNTER15 0xb0f
+#define CSR_MHPMCOUNTER16 0xb10
+#define CSR_MHPMCOUNTER17 0xb11
+#define CSR_MHPMCOUNTER18 0xb12
+#define CSR_MHPMCOUNTER19 0xb13
+#define CSR_MHPMCOUNTER20 0xb14
+#define CSR_MHPMCOUNTER21 0xb15
+#define CSR_MHPMCOUNTER22 0xb16
+#define CSR_MHPMCOUNTER23 0xb17
+#define CSR_MHPMCOUNTER24 0xb18
+#define CSR_MHPMCOUNTER25 0xb19
+#define CSR_MHPMCOUNTER26 0xb1a
+#define CSR_MHPMCOUNTER27 0xb1b
+#define CSR_MHPMCOUNTER28 0xb1c
+#define CSR_MHPMCOUNTER29 0xb1d
+#define CSR_MHPMCOUNTER30 0xb1e
+#define CSR_MHPMCOUNTER31 0xb1f
+#define CSR_MHPMEVENT3 0x323
+#define CSR_MHPMEVENT4 0x324
+#define CSR_MHPMEVENT5 0x325
+#define CSR_MHPMEVENT6 0x326
+#define CSR_MHPMEVENT7 0x327
+#define CSR_MHPMEVENT8 0x328
+#define CSR_MHPMEVENT9 0x329
+#define CSR_MHPMEVENT10 0x32a
+#define CSR_MHPMEVENT11 0x32b
+#define CSR_MHPMEVENT12 0x32c
+#define CSR_MHPMEVENT13 0x32d
+#define CSR_MHPMEVENT14 0x32e
+#define CSR_MHPMEVENT15 0x32f
+#define CSR_MHPMEVENT16 0x330
+#define CSR_MHPMEVENT17 0x331
+#define CSR_MHPMEVENT18 0x332
+#define CSR_MHPMEVENT19 0x333
+#define CSR_MHPMEVENT20 0x334
+#define CSR_MHPMEVENT21 0x335
+#define CSR_MHPMEVENT22 0x336
+#define CSR_MHPMEVENT23 0x337
+#define CSR_MHPMEVENT24 0x338
+#define CSR_MHPMEVENT25 0x339
+#define CSR_MHPMEVENT26 0x33a
+#define CSR_MHPMEVENT27 0x33b
+#define CSR_MHPMEVENT28 0x33c
+#define CSR_MHPMEVENT29 0x33d
+#define CSR_MHPMEVENT30 0x33e
+#define CSR_MHPMEVENT31 0x33f
+#define CSR_MVENDORID 0xf11
+#define CSR_MARCHID 0xf12
+#define CSR_MIMPID 0xf13
+#define CSR_MHARTID 0xf14
+#define CSR_CYCLEH 0xc80
+#define CSR_TIMEH 0xc81
+#define CSR_INSTRETH 0xc82
+#define CSR_HPMCOUNTER3H 0xc83
+#define CSR_HPMCOUNTER4H 0xc84
+#define CSR_HPMCOUNTER5H 0xc85
+#define CSR_HPMCOUNTER6H 0xc86
+#define CSR_HPMCOUNTER7H 0xc87
+#define CSR_HPMCOUNTER8H 0xc88
+#define CSR_HPMCOUNTER9H 0xc89
+#define CSR_HPMCOUNTER10H 0xc8a
+#define CSR_HPMCOUNTER11H 0xc8b
+#define CSR_HPMCOUNTER12H 0xc8c
+#define CSR_HPMCOUNTER13H 0xc8d
+#define CSR_HPMCOUNTER14H 0xc8e
+#define CSR_HPMCOUNTER15H 0xc8f
+#define CSR_HPMCOUNTER16H 0xc90
+#define CSR_HPMCOUNTER17H 0xc91
+#define CSR_HPMCOUNTER18H 0xc92
+#define CSR_HPMCOUNTER19H 0xc93
+#define CSR_HPMCOUNTER20H 0xc94
+#define CSR_HPMCOUNTER21H 0xc95
+#define CSR_HPMCOUNTER22H 0xc96
+#define CSR_HPMCOUNTER23H 0xc97
+#define CSR_HPMCOUNTER24H 0xc98
+#define CSR_HPMCOUNTER25H 0xc99
+#define CSR_HPMCOUNTER26H 0xc9a
+#define CSR_HPMCOUNTER27H 0xc9b
+#define CSR_HPMCOUNTER28H 0xc9c
+#define CSR_HPMCOUNTER29H 0xc9d
+#define CSR_HPMCOUNTER30H 0xc9e
+#define CSR_HPMCOUNTER31H 0xc9f
+#define CSR_MCYCLEH 0xb80
+#define CSR_MINSTRETH 0xb82
+#define CSR_MHPMCOUNTER3H 0xb83
+#define CSR_MHPMCOUNTER4H 0xb84
+#define CSR_MHPMCOUNTER5H 0xb85
+#define CSR_MHPMCOUNTER6H 0xb86
+#define CSR_MHPMCOUNTER7H 0xb87
+#define CSR_MHPMCOUNTER8H 0xb88
+#define CSR_MHPMCOUNTER9H 0xb89
+#define CSR_MHPMCOUNTER10H 0xb8a
+#define CSR_MHPMCOUNTER11H 0xb8b
+#define CSR_MHPMCOUNTER12H 0xb8c
+#define CSR_MHPMCOUNTER13H 0xb8d
+#define CSR_MHPMCOUNTER14H 0xb8e
+#define CSR_MHPMCOUNTER15H 0xb8f
+#define CSR_MHPMCOUNTER16H 0xb90
+#define CSR_MHPMCOUNTER17H 0xb91
+#define CSR_MHPMCOUNTER18H 0xb92
+#define CSR_MHPMCOUNTER19H 0xb93
+#define CSR_MHPMCOUNTER20H 0xb94
+#define CSR_MHPMCOUNTER21H 0xb95
+#define CSR_MHPMCOUNTER22H 0xb96
+#define CSR_MHPMCOUNTER23H 0xb97
+#define CSR_MHPMCOUNTER24H 0xb98
+#define CSR_MHPMCOUNTER25H 0xb99
+#define CSR_MHPMCOUNTER26H 0xb9a
+#define CSR_MHPMCOUNTER27H 0xb9b
+#define CSR_MHPMCOUNTER28H 0xb9c
+#define CSR_MHPMCOUNTER29H 0xb9d
+#define CSR_MHPMCOUNTER30H 0xb9e
+#define CSR_MHPMCOUNTER31H 0xb9f
+#define CAUSE_MISALIGNED_FETCH 0x0
+#define CAUSE_FETCH_ACCESS 0x1
+#define CAUSE_ILLEGAL_INSTRUCTION 0x2
+#define CAUSE_BREAKPOINT 0x3
+#define CAUSE_MISALIGNED_LOAD 0x4
+#define CAUSE_LOAD_ACCESS 0x5
+#define CAUSE_MISALIGNED_STORE 0x6
+#define CAUSE_STORE_ACCESS 0x7
+#define CAUSE_USER_ECALL 0x8
+#define CAUSE_SUPERVISOR_ECALL 0x9
+#define CAUSE_HYPERVISOR_ECALL 0xa
+#define CAUSE_MACHINE_ECALL 0xb
+#define CAUSE_FETCH_PAGE_FAULT 0xc
+#define CAUSE_LOAD_PAGE_FAULT 0xd
+#define CAUSE_STORE_PAGE_FAULT 0xf
+#endif
+#ifdef DECLARE_INSN
+DECLARE_INSN(beq, MATCH_BEQ, MASK_BEQ)
+DECLARE_INSN(bne, MATCH_BNE, MASK_BNE)
+DECLARE_INSN(blt, MATCH_BLT, MASK_BLT)
+DECLARE_INSN(bge, MATCH_BGE, MASK_BGE)
+DECLARE_INSN(bltu, MATCH_BLTU, MASK_BLTU)
+DECLARE_INSN(bgeu, MATCH_BGEU, MASK_BGEU)
+DECLARE_INSN(jalr, MATCH_JALR, MASK_JALR)
+DECLARE_INSN(jal, MATCH_JAL, MASK_JAL)
+DECLARE_INSN(lui, MATCH_LUI, MASK_LUI)
+DECLARE_INSN(auipc, MATCH_AUIPC, MASK_AUIPC)
+DECLARE_INSN(addi, MATCH_ADDI, MASK_ADDI)
+DECLARE_INSN(slli, MATCH_SLLI, MASK_SLLI)
+DECLARE_INSN(slti, MATCH_SLTI, MASK_SLTI)
+DECLARE_INSN(sltiu, MATCH_SLTIU, MASK_SLTIU)
+DECLARE_INSN(xori, MATCH_XORI, MASK_XORI)
+DECLARE_INSN(srli, MATCH_SRLI, MASK_SRLI)
+DECLARE_INSN(srai, MATCH_SRAI, MASK_SRAI)
+DECLARE_INSN(ori, MATCH_ORI, MASK_ORI)
+DECLARE_INSN(andi, MATCH_ANDI, MASK_ANDI)
+DECLARE_INSN(add, MATCH_ADD, MASK_ADD)
+DECLARE_INSN(sub, MATCH_SUB, MASK_SUB)
+DECLARE_INSN(sll, MATCH_SLL, MASK_SLL)
+DECLARE_INSN(slt, MATCH_SLT, MASK_SLT)
+DECLARE_INSN(sltu, MATCH_SLTU, MASK_SLTU)
+DECLARE_INSN(xor, MATCH_XOR, MASK_XOR)
+DECLARE_INSN(srl, MATCH_SRL, MASK_SRL)
+DECLARE_INSN(sra, MATCH_SRA, MASK_SRA)
+DECLARE_INSN(or, MATCH_OR, MASK_OR)
+DECLARE_INSN(and, MATCH_AND, MASK_AND)
+DECLARE_INSN(addiw, MATCH_ADDIW, MASK_ADDIW)
+DECLARE_INSN(slliw, MATCH_SLLIW, MASK_SLLIW)
+DECLARE_INSN(srliw, MATCH_SRLIW, MASK_SRLIW)
+DECLARE_INSN(sraiw, MATCH_SRAIW, MASK_SRAIW)
+DECLARE_INSN(addw, MATCH_ADDW, MASK_ADDW)
+DECLARE_INSN(subw, MATCH_SUBW, MASK_SUBW)
+DECLARE_INSN(sllw, MATCH_SLLW, MASK_SLLW)
+DECLARE_INSN(srlw, MATCH_SRLW, MASK_SRLW)
+DECLARE_INSN(sraw, MATCH_SRAW, MASK_SRAW)
+DECLARE_INSN(lb, MATCH_LB, MASK_LB)
+DECLARE_INSN(lh, MATCH_LH, MASK_LH)
+DECLARE_INSN(lw, MATCH_LW, MASK_LW)
+DECLARE_INSN(ld, MATCH_LD, MASK_LD)
+DECLARE_INSN(lbu, MATCH_LBU, MASK_LBU)
+DECLARE_INSN(lhu, MATCH_LHU, MASK_LHU)
+DECLARE_INSN(lwu, MATCH_LWU, MASK_LWU)
+DECLARE_INSN(sb, MATCH_SB, MASK_SB)
+DECLARE_INSN(sh, MATCH_SH, MASK_SH)
+DECLARE_INSN(sw, MATCH_SW, MASK_SW)
+DECLARE_INSN(sd, MATCH_SD, MASK_SD)
+DECLARE_INSN(fence, MATCH_FENCE, MASK_FENCE)
+DECLARE_INSN(fence_i, MATCH_FENCE_I, MASK_FENCE_I)
+DECLARE_INSN(mul, MATCH_MUL, MASK_MUL)
+DECLARE_INSN(mulh, MATCH_MULH, MASK_MULH)
+DECLARE_INSN(mulhsu, MATCH_MULHSU, MASK_MULHSU)
+DECLARE_INSN(mulhu, MATCH_MULHU, MASK_MULHU)
+DECLARE_INSN(div, MATCH_DIV, MASK_DIV)
+DECLARE_INSN(divu, MATCH_DIVU, MASK_DIVU)
+DECLARE_INSN(rem, MATCH_REM, MASK_REM)
+DECLARE_INSN(remu, MATCH_REMU, MASK_REMU)
+DECLARE_INSN(mulw, MATCH_MULW, MASK_MULW)
+DECLARE_INSN(divw, MATCH_DIVW, MASK_DIVW)
+DECLARE_INSN(divuw, MATCH_DIVUW, MASK_DIVUW)
+DECLARE_INSN(remw, MATCH_REMW, MASK_REMW)
+DECLARE_INSN(remuw, MATCH_REMUW, MASK_REMUW)
+DECLARE_INSN(amoadd_w, MATCH_AMOADD_W, MASK_AMOADD_W)
+DECLARE_INSN(amoxor_w, MATCH_AMOXOR_W, MASK_AMOXOR_W)
+DECLARE_INSN(amoor_w, MATCH_AMOOR_W, MASK_AMOOR_W)
+DECLARE_INSN(amoand_w, MATCH_AMOAND_W, MASK_AMOAND_W)
+DECLARE_INSN(amomin_w, MATCH_AMOMIN_W, MASK_AMOMIN_W)
+DECLARE_INSN(amomax_w, MATCH_AMOMAX_W, MASK_AMOMAX_W)
+DECLARE_INSN(amominu_w, MATCH_AMOMINU_W, MASK_AMOMINU_W)
+DECLARE_INSN(amomaxu_w, MATCH_AMOMAXU_W, MASK_AMOMAXU_W)
+DECLARE_INSN(amoswap_w, MATCH_AMOSWAP_W, MASK_AMOSWAP_W)
+DECLARE_INSN(lr_w, MATCH_LR_W, MASK_LR_W)
+DECLARE_INSN(sc_w, MATCH_SC_W, MASK_SC_W)
+DECLARE_INSN(amoadd_d, MATCH_AMOADD_D, MASK_AMOADD_D)
+DECLARE_INSN(amoxor_d, MATCH_AMOXOR_D, MASK_AMOXOR_D)
+DECLARE_INSN(amoor_d, MATCH_AMOOR_D, MASK_AMOOR_D)
+DECLARE_INSN(amoand_d, MATCH_AMOAND_D, MASK_AMOAND_D)
+DECLARE_INSN(amomin_d, MATCH_AMOMIN_D, MASK_AMOMIN_D)
+DECLARE_INSN(amomax_d, MATCH_AMOMAX_D, MASK_AMOMAX_D)
+DECLARE_INSN(amominu_d, MATCH_AMOMINU_D, MASK_AMOMINU_D)
+DECLARE_INSN(amomaxu_d, MATCH_AMOMAXU_D, MASK_AMOMAXU_D)
+DECLARE_INSN(amoswap_d, MATCH_AMOSWAP_D, MASK_AMOSWAP_D)
+DECLARE_INSN(lr_d, MATCH_LR_D, MASK_LR_D)
+DECLARE_INSN(sc_d, MATCH_SC_D, MASK_SC_D)
+DECLARE_INSN(ecall, MATCH_ECALL, MASK_ECALL)
+DECLARE_INSN(ebreak, MATCH_EBREAK, MASK_EBREAK)
+DECLARE_INSN(uret, MATCH_URET, MASK_URET)
+DECLARE_INSN(sret, MATCH_SRET, MASK_SRET)
+DECLARE_INSN(mret, MATCH_MRET, MASK_MRET)
+DECLARE_INSN(dret, MATCH_DRET, MASK_DRET)
+DECLARE_INSN(sfence_vma, MATCH_SFENCE_VMA, MASK_SFENCE_VMA)
+DECLARE_INSN(wfi, MATCH_WFI, MASK_WFI)
+DECLARE_INSN(csrrw, MATCH_CSRRW, MASK_CSRRW)
+DECLARE_INSN(csrrs, MATCH_CSRRS, MASK_CSRRS)
+DECLARE_INSN(csrrc, MATCH_CSRRC, MASK_CSRRC)
+DECLARE_INSN(csrrwi, MATCH_CSRRWI, MASK_CSRRWI)
+DECLARE_INSN(csrrsi, MATCH_CSRRSI, MASK_CSRRSI)
+DECLARE_INSN(csrrci, MATCH_CSRRCI, MASK_CSRRCI)
+DECLARE_INSN(fadd_s, MATCH_FADD_S, MASK_FADD_S)
+DECLARE_INSN(fsub_s, MATCH_FSUB_S, MASK_FSUB_S)
+DECLARE_INSN(fmul_s, MATCH_FMUL_S, MASK_FMUL_S)
+DECLARE_INSN(fdiv_s, MATCH_FDIV_S, MASK_FDIV_S)
+DECLARE_INSN(fsgnj_s, MATCH_FSGNJ_S, MASK_FSGNJ_S)
+DECLARE_INSN(fsgnjn_s, MATCH_FSGNJN_S, MASK_FSGNJN_S)
+DECLARE_INSN(fsgnjx_s, MATCH_FSGNJX_S, MASK_FSGNJX_S)
+DECLARE_INSN(fmin_s, MATCH_FMIN_S, MASK_FMIN_S)
+DECLARE_INSN(fmax_s, MATCH_FMAX_S, MASK_FMAX_S)
+DECLARE_INSN(fsqrt_s, MATCH_FSQRT_S, MASK_FSQRT_S)
+DECLARE_INSN(fadd_d, MATCH_FADD_D, MASK_FADD_D)
+DECLARE_INSN(fsub_d, MATCH_FSUB_D, MASK_FSUB_D)
+DECLARE_INSN(fmul_d, MATCH_FMUL_D, MASK_FMUL_D)
+DECLARE_INSN(fdiv_d, MATCH_FDIV_D, MASK_FDIV_D)
+DECLARE_INSN(fsgnj_d, MATCH_FSGNJ_D, MASK_FSGNJ_D)
+DECLARE_INSN(fsgnjn_d, MATCH_FSGNJN_D, MASK_FSGNJN_D)
+DECLARE_INSN(fsgnjx_d, MATCH_FSGNJX_D, MASK_FSGNJX_D)
+DECLARE_INSN(fmin_d, MATCH_FMIN_D, MASK_FMIN_D)
+DECLARE_INSN(fmax_d, MATCH_FMAX_D, MASK_FMAX_D)
+DECLARE_INSN(fcvt_s_d, MATCH_FCVT_S_D, MASK_FCVT_S_D)
+DECLARE_INSN(fcvt_d_s, MATCH_FCVT_D_S, MASK_FCVT_D_S)
+DECLARE_INSN(fsqrt_d, MATCH_FSQRT_D, MASK_FSQRT_D)
+DECLARE_INSN(fadd_q, MATCH_FADD_Q, MASK_FADD_Q)
+DECLARE_INSN(fsub_q, MATCH_FSUB_Q, MASK_FSUB_Q)
+DECLARE_INSN(fmul_q, MATCH_FMUL_Q, MASK_FMUL_Q)
+DECLARE_INSN(fdiv_q, MATCH_FDIV_Q, MASK_FDIV_Q)
+DECLARE_INSN(fsgnj_q, MATCH_FSGNJ_Q, MASK_FSGNJ_Q)
+DECLARE_INSN(fsgnjn_q, MATCH_FSGNJN_Q, MASK_FSGNJN_Q)
+DECLARE_INSN(fsgnjx_q, MATCH_FSGNJX_Q, MASK_FSGNJX_Q)
+DECLARE_INSN(fmin_q, MATCH_FMIN_Q, MASK_FMIN_Q)
+DECLARE_INSN(fmax_q, MATCH_FMAX_Q, MASK_FMAX_Q)
+DECLARE_INSN(fcvt_s_q, MATCH_FCVT_S_Q, MASK_FCVT_S_Q)
+DECLARE_INSN(fcvt_q_s, MATCH_FCVT_Q_S, MASK_FCVT_Q_S)
+DECLARE_INSN(fcvt_d_q, MATCH_FCVT_D_Q, MASK_FCVT_D_Q)
+DECLARE_INSN(fcvt_q_d, MATCH_FCVT_Q_D, MASK_FCVT_Q_D)
+DECLARE_INSN(fsqrt_q, MATCH_FSQRT_Q, MASK_FSQRT_Q)
+DECLARE_INSN(fle_s, MATCH_FLE_S, MASK_FLE_S)
+DECLARE_INSN(flt_s, MATCH_FLT_S, MASK_FLT_S)
+DECLARE_INSN(feq_s, MATCH_FEQ_S, MASK_FEQ_S)
+DECLARE_INSN(fle_d, MATCH_FLE_D, MASK_FLE_D)
+DECLARE_INSN(flt_d, MATCH_FLT_D, MASK_FLT_D)
+DECLARE_INSN(feq_d, MATCH_FEQ_D, MASK_FEQ_D)
+DECLARE_INSN(fle_q, MATCH_FLE_Q, MASK_FLE_Q)
+DECLARE_INSN(flt_q, MATCH_FLT_Q, MASK_FLT_Q)
+DECLARE_INSN(feq_q, MATCH_FEQ_Q, MASK_FEQ_Q)
+DECLARE_INSN(fcvt_w_s, MATCH_FCVT_W_S, MASK_FCVT_W_S)
+DECLARE_INSN(fcvt_wu_s, MATCH_FCVT_WU_S, MASK_FCVT_WU_S)
+DECLARE_INSN(fcvt_l_s, MATCH_FCVT_L_S, MASK_FCVT_L_S)
+DECLARE_INSN(fcvt_lu_s, MATCH_FCVT_LU_S, MASK_FCVT_LU_S)
+DECLARE_INSN(fmv_x_w, MATCH_FMV_X_W, MASK_FMV_X_W)
+DECLARE_INSN(fclass_s, MATCH_FCLASS_S, MASK_FCLASS_S)
+DECLARE_INSN(fcvt_w_d, MATCH_FCVT_W_D, MASK_FCVT_W_D)
+DECLARE_INSN(fcvt_wu_d, MATCH_FCVT_WU_D, MASK_FCVT_WU_D)
+DECLARE_INSN(fcvt_l_d, MATCH_FCVT_L_D, MASK_FCVT_L_D)
+DECLARE_INSN(fcvt_lu_d, MATCH_FCVT_LU_D, MASK_FCVT_LU_D)
+DECLARE_INSN(fmv_x_d, MATCH_FMV_X_D, MASK_FMV_X_D)
+DECLARE_INSN(fclass_d, MATCH_FCLASS_D, MASK_FCLASS_D)
+DECLARE_INSN(fcvt_w_q, MATCH_FCVT_W_Q, MASK_FCVT_W_Q)
+DECLARE_INSN(fcvt_wu_q, MATCH_FCVT_WU_Q, MASK_FCVT_WU_Q)
+DECLARE_INSN(fcvt_l_q, MATCH_FCVT_L_Q, MASK_FCVT_L_Q)
+DECLARE_INSN(fcvt_lu_q, MATCH_FCVT_LU_Q, MASK_FCVT_LU_Q)
+DECLARE_INSN(fmv_x_q, MATCH_FMV_X_Q, MASK_FMV_X_Q)
+DECLARE_INSN(fclass_q, MATCH_FCLASS_Q, MASK_FCLASS_Q)
+DECLARE_INSN(fcvt_s_w, MATCH_FCVT_S_W, MASK_FCVT_S_W)
+DECLARE_INSN(fcvt_s_wu, MATCH_FCVT_S_WU, MASK_FCVT_S_WU)
+DECLARE_INSN(fcvt_s_l, MATCH_FCVT_S_L, MASK_FCVT_S_L)
+DECLARE_INSN(fcvt_s_lu, MATCH_FCVT_S_LU, MASK_FCVT_S_LU)
+DECLARE_INSN(fmv_w_x, MATCH_FMV_W_X, MASK_FMV_W_X)
+DECLARE_INSN(fcvt_d_w, MATCH_FCVT_D_W, MASK_FCVT_D_W)
+DECLARE_INSN(fcvt_d_wu, MATCH_FCVT_D_WU, MASK_FCVT_D_WU)
+DECLARE_INSN(fcvt_d_l, MATCH_FCVT_D_L, MASK_FCVT_D_L)
+DECLARE_INSN(fcvt_d_lu, MATCH_FCVT_D_LU, MASK_FCVT_D_LU)
+DECLARE_INSN(fmv_d_x, MATCH_FMV_D_X, MASK_FMV_D_X)
+DECLARE_INSN(fcvt_q_w, MATCH_FCVT_Q_W, MASK_FCVT_Q_W)
+DECLARE_INSN(fcvt_q_wu, MATCH_FCVT_Q_WU, MASK_FCVT_Q_WU)
+DECLARE_INSN(fcvt_q_l, MATCH_FCVT_Q_L, MASK_FCVT_Q_L)
+DECLARE_INSN(fcvt_q_lu, MATCH_FCVT_Q_LU, MASK_FCVT_Q_LU)
+DECLARE_INSN(fmv_q_x, MATCH_FMV_Q_X, MASK_FMV_Q_X)
+DECLARE_INSN(flw, MATCH_FLW, MASK_FLW)
+DECLARE_INSN(fld, MATCH_FLD, MASK_FLD)
+DECLARE_INSN(flq, MATCH_FLQ, MASK_FLQ)
+DECLARE_INSN(fsw, MATCH_FSW, MASK_FSW)
+DECLARE_INSN(fsd, MATCH_FSD, MASK_FSD)
+DECLARE_INSN(fsq, MATCH_FSQ, MASK_FSQ)
+DECLARE_INSN(fmadd_s, MATCH_FMADD_S, MASK_FMADD_S)
+DECLARE_INSN(fmsub_s, MATCH_FMSUB_S, MASK_FMSUB_S)
+DECLARE_INSN(fnmsub_s, MATCH_FNMSUB_S, MASK_FNMSUB_S)
+DECLARE_INSN(fnmadd_s, MATCH_FNMADD_S, MASK_FNMADD_S)
+DECLARE_INSN(fmadd_d, MATCH_FMADD_D, MASK_FMADD_D)
+DECLARE_INSN(fmsub_d, MATCH_FMSUB_D, MASK_FMSUB_D)
+DECLARE_INSN(fnmsub_d, MATCH_FNMSUB_D, MASK_FNMSUB_D)
+DECLARE_INSN(fnmadd_d, MATCH_FNMADD_D, MASK_FNMADD_D)
+DECLARE_INSN(fmadd_q, MATCH_FMADD_Q, MASK_FMADD_Q)
+DECLARE_INSN(fmsub_q, MATCH_FMSUB_Q, MASK_FMSUB_Q)
+DECLARE_INSN(fnmsub_q, MATCH_FNMSUB_Q, MASK_FNMSUB_Q)
+DECLARE_INSN(fnmadd_q, MATCH_FNMADD_Q, MASK_FNMADD_Q)
+DECLARE_INSN(c_nop, MATCH_C_NOP, MASK_C_NOP)
+DECLARE_INSN(c_addi16sp, MATCH_C_ADDI16SP, MASK_C_ADDI16SP)
+DECLARE_INSN(c_jr, MATCH_C_JR, MASK_C_JR)
+DECLARE_INSN(c_jalr, MATCH_C_JALR, MASK_C_JALR)
+DECLARE_INSN(c_ebreak, MATCH_C_EBREAK, MASK_C_EBREAK)
+DECLARE_INSN(c_ld, MATCH_C_LD, MASK_C_LD)
+DECLARE_INSN(c_sd, MATCH_C_SD, MASK_C_SD)
+DECLARE_INSN(c_addiw, MATCH_C_ADDIW, MASK_C_ADDIW)
+DECLARE_INSN(c_ldsp, MATCH_C_LDSP, MASK_C_LDSP)
+DECLARE_INSN(c_sdsp, MATCH_C_SDSP, MASK_C_SDSP)
+DECLARE_INSN(c_addi4spn, MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN)
+DECLARE_INSN(c_fld, MATCH_C_FLD, MASK_C_FLD)
+DECLARE_INSN(c_lw, MATCH_C_LW, MASK_C_LW)
+DECLARE_INSN(c_flw, MATCH_C_FLW, MASK_C_FLW)
+DECLARE_INSN(c_fsd, MATCH_C_FSD, MASK_C_FSD)
+DECLARE_INSN(c_sw, MATCH_C_SW, MASK_C_SW)
+DECLARE_INSN(c_fsw, MATCH_C_FSW, MASK_C_FSW)
+DECLARE_INSN(c_addi, MATCH_C_ADDI, MASK_C_ADDI)
+DECLARE_INSN(c_jal, MATCH_C_JAL, MASK_C_JAL)
+DECLARE_INSN(c_li, MATCH_C_LI, MASK_C_LI)
+DECLARE_INSN(c_lui, MATCH_C_LUI, MASK_C_LUI)
+DECLARE_INSN(c_srli, MATCH_C_SRLI, MASK_C_SRLI)
+DECLARE_INSN(c_srai, MATCH_C_SRAI, MASK_C_SRAI)
+DECLARE_INSN(c_andi, MATCH_C_ANDI, MASK_C_ANDI)
+DECLARE_INSN(c_sub, MATCH_C_SUB, MASK_C_SUB)
+DECLARE_INSN(c_xor, MATCH_C_XOR, MASK_C_XOR)
+DECLARE_INSN(c_or, MATCH_C_OR, MASK_C_OR)
+DECLARE_INSN(c_and, MATCH_C_AND, MASK_C_AND)
+DECLARE_INSN(c_subw, MATCH_C_SUBW, MASK_C_SUBW)
+DECLARE_INSN(c_addw, MATCH_C_ADDW, MASK_C_ADDW)
+DECLARE_INSN(c_j, MATCH_C_J, MASK_C_J)
+DECLARE_INSN(c_beqz, MATCH_C_BEQZ, MASK_C_BEQZ)
+DECLARE_INSN(c_bnez, MATCH_C_BNEZ, MASK_C_BNEZ)
+DECLARE_INSN(c_slli, MATCH_C_SLLI, MASK_C_SLLI)
+DECLARE_INSN(c_fldsp, MATCH_C_FLDSP, MASK_C_FLDSP)
+DECLARE_INSN(c_lwsp, MATCH_C_LWSP, MASK_C_LWSP)
+DECLARE_INSN(c_flwsp, MATCH_C_FLWSP, MASK_C_FLWSP)
+DECLARE_INSN(c_mv, MATCH_C_MV, MASK_C_MV)
+DECLARE_INSN(c_add, MATCH_C_ADD, MASK_C_ADD)
+DECLARE_INSN(c_fsdsp, MATCH_C_FSDSP, MASK_C_FSDSP)
+DECLARE_INSN(c_swsp, MATCH_C_SWSP, MASK_C_SWSP)
+DECLARE_INSN(c_fswsp, MATCH_C_FSWSP, MASK_C_FSWSP)
+DECLARE_INSN(custom0, MATCH_CUSTOM0, MASK_CUSTOM0)
+DECLARE_INSN(custom0_rs1, MATCH_CUSTOM0_RS1, MASK_CUSTOM0_RS1)
+DECLARE_INSN(custom0_rs1_rs2, MATCH_CUSTOM0_RS1_RS2, MASK_CUSTOM0_RS1_RS2)
+DECLARE_INSN(custom0_rd, MATCH_CUSTOM0_RD, MASK_CUSTOM0_RD)
+DECLARE_INSN(custom0_rd_rs1, MATCH_CUSTOM0_RD_RS1, MASK_CUSTOM0_RD_RS1)
+DECLARE_INSN(custom0_rd_rs1_rs2, MATCH_CUSTOM0_RD_RS1_RS2, MASK_CUSTOM0_RD_RS1_RS2)
+DECLARE_INSN(custom1, MATCH_CUSTOM1, MASK_CUSTOM1)
+DECLARE_INSN(custom1_rs1, MATCH_CUSTOM1_RS1, MASK_CUSTOM1_RS1)
+DECLARE_INSN(custom1_rs1_rs2, MATCH_CUSTOM1_RS1_RS2, MASK_CUSTOM1_RS1_RS2)
+DECLARE_INSN(custom1_rd, MATCH_CUSTOM1_RD, MASK_CUSTOM1_RD)
+DECLARE_INSN(custom1_rd_rs1, MATCH_CUSTOM1_RD_RS1, MASK_CUSTOM1_RD_RS1)
+DECLARE_INSN(custom1_rd_rs1_rs2, MATCH_CUSTOM1_RD_RS1_RS2, MASK_CUSTOM1_RD_RS1_RS2)
+DECLARE_INSN(custom2, MATCH_CUSTOM2, MASK_CUSTOM2)
+DECLARE_INSN(custom2_rs1, MATCH_CUSTOM2_RS1, MASK_CUSTOM2_RS1)
+DECLARE_INSN(custom2_rs1_rs2, MATCH_CUSTOM2_RS1_RS2, MASK_CUSTOM2_RS1_RS2)
+DECLARE_INSN(custom2_rd, MATCH_CUSTOM2_RD, MASK_CUSTOM2_RD)
+DECLARE_INSN(custom2_rd_rs1, MATCH_CUSTOM2_RD_RS1, MASK_CUSTOM2_RD_RS1)
+DECLARE_INSN(custom2_rd_rs1_rs2, MATCH_CUSTOM2_RD_RS1_RS2, MASK_CUSTOM2_RD_RS1_RS2)
+DECLARE_INSN(custom3, MATCH_CUSTOM3, MASK_CUSTOM3)
+DECLARE_INSN(custom3_rs1, MATCH_CUSTOM3_RS1, MASK_CUSTOM3_RS1)
+DECLARE_INSN(custom3_rs1_rs2, MATCH_CUSTOM3_RS1_RS2, MASK_CUSTOM3_RS1_RS2)
+DECLARE_INSN(custom3_rd, MATCH_CUSTOM3_RD, MASK_CUSTOM3_RD)
+DECLARE_INSN(custom3_rd_rs1, MATCH_CUSTOM3_RD_RS1, MASK_CUSTOM3_RD_RS1)
+DECLARE_INSN(custom3_rd_rs1_rs2, MATCH_CUSTOM3_RD_RS1_RS2, MASK_CUSTOM3_RD_RS1_RS2)
+#endif
+#ifdef DECLARE_CSR
+DECLARE_CSR(fflags, CSR_FFLAGS)
+DECLARE_CSR(frm, CSR_FRM)
+DECLARE_CSR(fcsr, CSR_FCSR)
+DECLARE_CSR(cycle, CSR_CYCLE)
+DECLARE_CSR(time, CSR_TIME)
+DECLARE_CSR(instret, CSR_INSTRET)
+DECLARE_CSR(hpmcounter3, CSR_HPMCOUNTER3)
+DECLARE_CSR(hpmcounter4, CSR_HPMCOUNTER4)
+DECLARE_CSR(hpmcounter5, CSR_HPMCOUNTER5)
+DECLARE_CSR(hpmcounter6, CSR_HPMCOUNTER6)
+DECLARE_CSR(hpmcounter7, CSR_HPMCOUNTER7)
+DECLARE_CSR(hpmcounter8, CSR_HPMCOUNTER8)
+DECLARE_CSR(hpmcounter9, CSR_HPMCOUNTER9)
+DECLARE_CSR(hpmcounter10, CSR_HPMCOUNTER10)
+DECLARE_CSR(hpmcounter11, CSR_HPMCOUNTER11)
+DECLARE_CSR(hpmcounter12, CSR_HPMCOUNTER12)
+DECLARE_CSR(hpmcounter13, CSR_HPMCOUNTER13)
+DECLARE_CSR(hpmcounter14, CSR_HPMCOUNTER14)
+DECLARE_CSR(hpmcounter15, CSR_HPMCOUNTER15)
+DECLARE_CSR(hpmcounter16, CSR_HPMCOUNTER16)
+DECLARE_CSR(hpmcounter17, CSR_HPMCOUNTER17)
+DECLARE_CSR(hpmcounter18, CSR_HPMCOUNTER18)
+DECLARE_CSR(hpmcounter19, CSR_HPMCOUNTER19)
+DECLARE_CSR(hpmcounter20, CSR_HPMCOUNTER20)
+DECLARE_CSR(hpmcounter21, CSR_HPMCOUNTER21)
+DECLARE_CSR(hpmcounter22, CSR_HPMCOUNTER22)
+DECLARE_CSR(hpmcounter23, CSR_HPMCOUNTER23)
+DECLARE_CSR(hpmcounter24, CSR_HPMCOUNTER24)
+DECLARE_CSR(hpmcounter25, CSR_HPMCOUNTER25)
+DECLARE_CSR(hpmcounter26, CSR_HPMCOUNTER26)
+DECLARE_CSR(hpmcounter27, CSR_HPMCOUNTER27)
+DECLARE_CSR(hpmcounter28, CSR_HPMCOUNTER28)
+DECLARE_CSR(hpmcounter29, CSR_HPMCOUNTER29)
+DECLARE_CSR(hpmcounter30, CSR_HPMCOUNTER30)
+DECLARE_CSR(hpmcounter31, CSR_HPMCOUNTER31)
+DECLARE_CSR(sstatus, CSR_SSTATUS)
+DECLARE_CSR(sie, CSR_SIE)
+DECLARE_CSR(stvec, CSR_STVEC)
+DECLARE_CSR(scounteren, CSR_SCOUNTEREN)
+DECLARE_CSR(sscratch, CSR_SSCRATCH)
+DECLARE_CSR(sepc, CSR_SEPC)
+DECLARE_CSR(scause, CSR_SCAUSE)
+DECLARE_CSR(stval, CSR_STVAL)
+DECLARE_CSR(sip, CSR_SIP)
+DECLARE_CSR(satp, CSR_SATP)
+DECLARE_CSR(mstatus, CSR_MSTATUS)
+DECLARE_CSR(misa, CSR_MISA)
+DECLARE_CSR(medeleg, CSR_MEDELEG)
+DECLARE_CSR(mideleg, CSR_MIDELEG)
+DECLARE_CSR(mie, CSR_MIE)
+DECLARE_CSR(mtvec, CSR_MTVEC)
+DECLARE_CSR(mcounteren, CSR_MCOUNTEREN)
+DECLARE_CSR(mscratch, CSR_MSCRATCH)
+DECLARE_CSR(mepc, CSR_MEPC)
+DECLARE_CSR(mcause, CSR_MCAUSE)
+DECLARE_CSR(mtval, CSR_MTVAL)
+DECLARE_CSR(mip, CSR_MIP)
+DECLARE_CSR(pmpcfg0, CSR_PMPCFG0)
+DECLARE_CSR(pmpcfg1, CSR_PMPCFG1)
+DECLARE_CSR(pmpcfg2, CSR_PMPCFG2)
+DECLARE_CSR(pmpcfg3, CSR_PMPCFG3)
+DECLARE_CSR(pmpaddr0, CSR_PMPADDR0)
+DECLARE_CSR(pmpaddr1, CSR_PMPADDR1)
+DECLARE_CSR(pmpaddr2, CSR_PMPADDR2)
+DECLARE_CSR(pmpaddr3, CSR_PMPADDR3)
+DECLARE_CSR(pmpaddr4, CSR_PMPADDR4)
+DECLARE_CSR(pmpaddr5, CSR_PMPADDR5)
+DECLARE_CSR(pmpaddr6, CSR_PMPADDR6)
+DECLARE_CSR(pmpaddr7, CSR_PMPADDR7)
+DECLARE_CSR(pmpaddr8, CSR_PMPADDR8)
+DECLARE_CSR(pmpaddr9, CSR_PMPADDR9)
+DECLARE_CSR(pmpaddr10, CSR_PMPADDR10)
+DECLARE_CSR(pmpaddr11, CSR_PMPADDR11)
+DECLARE_CSR(pmpaddr12, CSR_PMPADDR12)
+DECLARE_CSR(pmpaddr13, CSR_PMPADDR13)
+DECLARE_CSR(pmpaddr14, CSR_PMPADDR14)
+DECLARE_CSR(pmpaddr15, CSR_PMPADDR15)
+DECLARE_CSR(tselect, CSR_TSELECT)
+DECLARE_CSR(tdata1, CSR_TDATA1)
+DECLARE_CSR(tdata2, CSR_TDATA2)
+DECLARE_CSR(tdata3, CSR_TDATA3)
+DECLARE_CSR(dcsr, CSR_DCSR)
+DECLARE_CSR(dpc, CSR_DPC)
+DECLARE_CSR(dscratch, CSR_DSCRATCH)
+DECLARE_CSR(mcycle, CSR_MCYCLE)
+DECLARE_CSR(minstret, CSR_MINSTRET)
+DECLARE_CSR(mhpmcounter3, CSR_MHPMCOUNTER3)
+DECLARE_CSR(mhpmcounter4, CSR_MHPMCOUNTER4)
+DECLARE_CSR(mhpmcounter5, CSR_MHPMCOUNTER5)
+DECLARE_CSR(mhpmcounter6, CSR_MHPMCOUNTER6)
+DECLARE_CSR(mhpmcounter7, CSR_MHPMCOUNTER7)
+DECLARE_CSR(mhpmcounter8, CSR_MHPMCOUNTER8)
+DECLARE_CSR(mhpmcounter9, CSR_MHPMCOUNTER9)
+DECLARE_CSR(mhpmcounter10, CSR_MHPMCOUNTER10)
+DECLARE_CSR(mhpmcounter11, CSR_MHPMCOUNTER11)
+DECLARE_CSR(mhpmcounter12, CSR_MHPMCOUNTER12)
+DECLARE_CSR(mhpmcounter13, CSR_MHPMCOUNTER13)
+DECLARE_CSR(mhpmcounter14, CSR_MHPMCOUNTER14)
+DECLARE_CSR(mhpmcounter15, CSR_MHPMCOUNTER15)
+DECLARE_CSR(mhpmcounter16, CSR_MHPMCOUNTER16)
+DECLARE_CSR(mhpmcounter17, CSR_MHPMCOUNTER17)
+DECLARE_CSR(mhpmcounter18, CSR_MHPMCOUNTER18)
+DECLARE_CSR(mhpmcounter19, CSR_MHPMCOUNTER19)
+DECLARE_CSR(mhpmcounter20, CSR_MHPMCOUNTER20)
+DECLARE_CSR(mhpmcounter21, CSR_MHPMCOUNTER21)
+DECLARE_CSR(mhpmcounter22, CSR_MHPMCOUNTER22)
+DECLARE_CSR(mhpmcounter23, CSR_MHPMCOUNTER23)
+DECLARE_CSR(mhpmcounter24, CSR_MHPMCOUNTER24)
+DECLARE_CSR(mhpmcounter25, CSR_MHPMCOUNTER25)
+DECLARE_CSR(mhpmcounter26, CSR_MHPMCOUNTER26)
+DECLARE_CSR(mhpmcounter27, CSR_MHPMCOUNTER27)
+DECLARE_CSR(mhpmcounter28, CSR_MHPMCOUNTER28)
+DECLARE_CSR(mhpmcounter29, CSR_MHPMCOUNTER29)
+DECLARE_CSR(mhpmcounter30, CSR_MHPMCOUNTER30)
+DECLARE_CSR(mhpmcounter31, CSR_MHPMCOUNTER31)
+DECLARE_CSR(mhpmevent3, CSR_MHPMEVENT3)
+DECLARE_CSR(mhpmevent4, CSR_MHPMEVENT4)
+DECLARE_CSR(mhpmevent5, CSR_MHPMEVENT5)
+DECLARE_CSR(mhpmevent6, CSR_MHPMEVENT6)
+DECLARE_CSR(mhpmevent7, CSR_MHPMEVENT7)
+DECLARE_CSR(mhpmevent8, CSR_MHPMEVENT8)
+DECLARE_CSR(mhpmevent9, CSR_MHPMEVENT9)
+DECLARE_CSR(mhpmevent10, CSR_MHPMEVENT10)
+DECLARE_CSR(mhpmevent11, CSR_MHPMEVENT11)
+DECLARE_CSR(mhpmevent12, CSR_MHPMEVENT12)
+DECLARE_CSR(mhpmevent13, CSR_MHPMEVENT13)
+DECLARE_CSR(mhpmevent14, CSR_MHPMEVENT14)
+DECLARE_CSR(mhpmevent15, CSR_MHPMEVENT15)
+DECLARE_CSR(mhpmevent16, CSR_MHPMEVENT16)
+DECLARE_CSR(mhpmevent17, CSR_MHPMEVENT17)
+DECLARE_CSR(mhpmevent18, CSR_MHPMEVENT18)
+DECLARE_CSR(mhpmevent19, CSR_MHPMEVENT19)
+DECLARE_CSR(mhpmevent20, CSR_MHPMEVENT20)
+DECLARE_CSR(mhpmevent21, CSR_MHPMEVENT21)
+DECLARE_CSR(mhpmevent22, CSR_MHPMEVENT22)
+DECLARE_CSR(mhpmevent23, CSR_MHPMEVENT23)
+DECLARE_CSR(mhpmevent24, CSR_MHPMEVENT24)
+DECLARE_CSR(mhpmevent25, CSR_MHPMEVENT25)
+DECLARE_CSR(mhpmevent26, CSR_MHPMEVENT26)
+DECLARE_CSR(mhpmevent27, CSR_MHPMEVENT27)
+DECLARE_CSR(mhpmevent28, CSR_MHPMEVENT28)
+DECLARE_CSR(mhpmevent29, CSR_MHPMEVENT29)
+DECLARE_CSR(mhpmevent30, CSR_MHPMEVENT30)
+DECLARE_CSR(mhpmevent31, CSR_MHPMEVENT31)
+DECLARE_CSR(mvendorid, CSR_MVENDORID)
+DECLARE_CSR(marchid, CSR_MARCHID)
+DECLARE_CSR(mimpid, CSR_MIMPID)
+DECLARE_CSR(mhartid, CSR_MHARTID)
+DECLARE_CSR(cycleh, CSR_CYCLEH)
+DECLARE_CSR(timeh, CSR_TIMEH)
+DECLARE_CSR(instreth, CSR_INSTRETH)
+DECLARE_CSR(hpmcounter3h, CSR_HPMCOUNTER3H)
+DECLARE_CSR(hpmcounter4h, CSR_HPMCOUNTER4H)
+DECLARE_CSR(hpmcounter5h, CSR_HPMCOUNTER5H)
+DECLARE_CSR(hpmcounter6h, CSR_HPMCOUNTER6H)
+DECLARE_CSR(hpmcounter7h, CSR_HPMCOUNTER7H)
+DECLARE_CSR(hpmcounter8h, CSR_HPMCOUNTER8H)
+DECLARE_CSR(hpmcounter9h, CSR_HPMCOUNTER9H)
+DECLARE_CSR(hpmcounter10h, CSR_HPMCOUNTER10H)
+DECLARE_CSR(hpmcounter11h, CSR_HPMCOUNTER11H)
+DECLARE_CSR(hpmcounter12h, CSR_HPMCOUNTER12H)
+DECLARE_CSR(hpmcounter13h, CSR_HPMCOUNTER13H)
+DECLARE_CSR(hpmcounter14h, CSR_HPMCOUNTER14H)
+DECLARE_CSR(hpmcounter15h, CSR_HPMCOUNTER15H)
+DECLARE_CSR(hpmcounter16h, CSR_HPMCOUNTER16H)
+DECLARE_CSR(hpmcounter17h, CSR_HPMCOUNTER17H)
+DECLARE_CSR(hpmcounter18h, CSR_HPMCOUNTER18H)
+DECLARE_CSR(hpmcounter19h, CSR_HPMCOUNTER19H)
+DECLARE_CSR(hpmcounter20h, CSR_HPMCOUNTER20H)
+DECLARE_CSR(hpmcounter21h, CSR_HPMCOUNTER21H)
+DECLARE_CSR(hpmcounter22h, CSR_HPMCOUNTER22H)
+DECLARE_CSR(hpmcounter23h, CSR_HPMCOUNTER23H)
+DECLARE_CSR(hpmcounter24h, CSR_HPMCOUNTER24H)
+DECLARE_CSR(hpmcounter25h, CSR_HPMCOUNTER25H)
+DECLARE_CSR(hpmcounter26h, CSR_HPMCOUNTER26H)
+DECLARE_CSR(hpmcounter27h, CSR_HPMCOUNTER27H)
+DECLARE_CSR(hpmcounter28h, CSR_HPMCOUNTER28H)
+DECLARE_CSR(hpmcounter29h, CSR_HPMCOUNTER29H)
+DECLARE_CSR(hpmcounter30h, CSR_HPMCOUNTER30H)
+DECLARE_CSR(hpmcounter31h, CSR_HPMCOUNTER31H)
+DECLARE_CSR(mcycleh, CSR_MCYCLEH)
+DECLARE_CSR(minstreth, CSR_MINSTRETH)
+DECLARE_CSR(mhpmcounter3h, CSR_MHPMCOUNTER3H)
+DECLARE_CSR(mhpmcounter4h, CSR_MHPMCOUNTER4H)
+DECLARE_CSR(mhpmcounter5h, CSR_MHPMCOUNTER5H)
+DECLARE_CSR(mhpmcounter6h, CSR_MHPMCOUNTER6H)
+DECLARE_CSR(mhpmcounter7h, CSR_MHPMCOUNTER7H)
+DECLARE_CSR(mhpmcounter8h, CSR_MHPMCOUNTER8H)
+DECLARE_CSR(mhpmcounter9h, CSR_MHPMCOUNTER9H)
+DECLARE_CSR(mhpmcounter10h, CSR_MHPMCOUNTER10H)
+DECLARE_CSR(mhpmcounter11h, CSR_MHPMCOUNTER11H)
+DECLARE_CSR(mhpmcounter12h, CSR_MHPMCOUNTER12H)
+DECLARE_CSR(mhpmcounter13h, CSR_MHPMCOUNTER13H)
+DECLARE_CSR(mhpmcounter14h, CSR_MHPMCOUNTER14H)
+DECLARE_CSR(mhpmcounter15h, CSR_MHPMCOUNTER15H)
+DECLARE_CSR(mhpmcounter16h, CSR_MHPMCOUNTER16H)
+DECLARE_CSR(mhpmcounter17h, CSR_MHPMCOUNTER17H)
+DECLARE_CSR(mhpmcounter18h, CSR_MHPMCOUNTER18H)
+DECLARE_CSR(mhpmcounter19h, CSR_MHPMCOUNTER19H)
+DECLARE_CSR(mhpmcounter20h, CSR_MHPMCOUNTER20H)
+DECLARE_CSR(mhpmcounter21h, CSR_MHPMCOUNTER21H)
+DECLARE_CSR(mhpmcounter22h, CSR_MHPMCOUNTER22H)
+DECLARE_CSR(mhpmcounter23h, CSR_MHPMCOUNTER23H)
+DECLARE_CSR(mhpmcounter24h, CSR_MHPMCOUNTER24H)
+DECLARE_CSR(mhpmcounter25h, CSR_MHPMCOUNTER25H)
+DECLARE_CSR(mhpmcounter26h, CSR_MHPMCOUNTER26H)
+DECLARE_CSR(mhpmcounter27h, CSR_MHPMCOUNTER27H)
+DECLARE_CSR(mhpmcounter28h, CSR_MHPMCOUNTER28H)
+DECLARE_CSR(mhpmcounter29h, CSR_MHPMCOUNTER29H)
+DECLARE_CSR(mhpmcounter30h, CSR_MHPMCOUNTER30H)
+DECLARE_CSR(mhpmcounter31h, CSR_MHPMCOUNTER31H)
+#endif
+#ifdef DECLARE_CAUSE
+DECLARE_CAUSE("misaligned fetch", CAUSE_MISALIGNED_FETCH)
+DECLARE_CAUSE("fetch access", CAUSE_FETCH_ACCESS)
+DECLARE_CAUSE("illegal instruction", CAUSE_ILLEGAL_INSTRUCTION)
+DECLARE_CAUSE("breakpoint", CAUSE_BREAKPOINT)
+DECLARE_CAUSE("misaligned load", CAUSE_MISALIGNED_LOAD)
+DECLARE_CAUSE("load access", CAUSE_LOAD_ACCESS)
+DECLARE_CAUSE("misaligned store", CAUSE_MISALIGNED_STORE)
+DECLARE_CAUSE("store access", CAUSE_STORE_ACCESS)
+DECLARE_CAUSE("user_ecall", CAUSE_USER_ECALL)
+DECLARE_CAUSE("supervisor_ecall", CAUSE_SUPERVISOR_ECALL)
+DECLARE_CAUSE("hypervisor_ecall", CAUSE_HYPERVISOR_ECALL)
+DECLARE_CAUSE("machine_ecall", CAUSE_MACHINE_ECALL)
+DECLARE_CAUSE("fetch page fault", CAUSE_FETCH_PAGE_FAULT)
+DECLARE_CAUSE("load page fault", CAUSE_LOAD_PAGE_FAULT)
+DECLARE_CAUSE("store page fault", CAUSE_STORE_PAGE_FAULT)
+#endif
diff --git a/src/target/riscv/gdb_regs.h b/src/target/riscv/gdb_regs.h
new file mode 100644 (file)
index 0000000..a587952
--- /dev/null
@@ -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
diff --git a/src/target/riscv/opcodes.h b/src/target/riscv/opcodes.h
new file mode 100644 (file)
index 0000000..dd51c80
--- /dev/null
@@ -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);
+}
diff --git a/src/target/riscv/program.c b/src/target/riscv/program.c
new file mode 100644 (file)
index 0000000..5e899b2
--- /dev/null
@@ -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;
+}
diff --git a/src/target/riscv/program.h b/src/target/riscv/program.h
new file mode 100644 (file)
index 0000000..d641be1
--- /dev/null
@@ -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
diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c
new file mode 100644 (file)
index 0000000..f2a6869
--- /dev/null
@@ -0,0 +1,2328 @@
+/*
+ * Support for RISC-V, debug version 0.11. This was never an officially adopted
+ * spec, but SiFive made some silicon that uses it.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <time.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "target/target.h"
+#include "target/algorithm.h"
+#include "target/target_type.h"
+#include "log.h"
+#include "jtag/jtag.h"
+#include "target/register.h"
+#include "target/breakpoints.h"
+#include "helper/time_support.h"
+#include "riscv.h"
+#include "asm.h"
+#include "gdb_regs.h"
+
+/**
+ * Since almost everything can be accomplish by scanning the dbus register, all
+ * functions here assume dbus is already selected. The exception are functions
+ * called directly by OpenOCD, which can't assume anything about what's
+ * currently in IR. They should set IR to dbus explicitly.
+ */
+
+/**
+ * Code structure
+ *
+ * At the bottom of the stack are the OpenOCD JTAG functions:
+ *     jtag_add_[id]r_scan
+ *     jtag_execute_query
+ *     jtag_add_runtest
+ *
+ * There are a few functions to just instantly shift a register and get its
+ * value:
+ *    dtmcontrol_scan
+ *    idcode_scan
+ *    dbus_scan
+ *
+ * Because doing one scan and waiting for the result is slow, most functions
+ * batch up a bunch of dbus writes and then execute them all at once. They use
+ * the scans "class" for this:
+ *    scans_new
+ *    scans_delete
+ *    scans_execute
+ *    scans_add_...
+ * Usually you new(), call a bunch of add functions, then execute() and look
+ * at the results by calling scans_get...()
+ *
+ * Optimized functions will directly use the scans class above, but slightly
+ * lazier code will use the cache functions that in turn use the scans
+ * functions:
+ *    cache_get...
+ *    cache_set...
+ *    cache_write
+ * cache_set... update a local structure, which is then synced to the target
+ * with cache_write(). Only Debug RAM words that are actually changed are sent
+ * to the target. Afterwards use cache_get... to read results.
+ */
+
+#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
+#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
+
+#define DIM(x)         (sizeof(x)/sizeof(*x))
+
+/* Constants for legacy SiFive hardware breakpoints. */
+#define CSR_BPCONTROL_X                        (1<<0)
+#define CSR_BPCONTROL_W                        (1<<1)
+#define CSR_BPCONTROL_R                        (1<<2)
+#define CSR_BPCONTROL_U                        (1<<3)
+#define CSR_BPCONTROL_S                        (1<<4)
+#define CSR_BPCONTROL_H                        (1<<5)
+#define CSR_BPCONTROL_M                        (1<<6)
+#define CSR_BPCONTROL_BPMATCH  (0xf<<7)
+#define CSR_BPCONTROL_BPACTION (0xff<<11)
+
+#define DEBUG_ROM_START                0x800
+#define DEBUG_ROM_RESUME       (DEBUG_ROM_START + 4)
+#define DEBUG_ROM_EXCEPTION    (DEBUG_ROM_START + 8)
+#define DEBUG_RAM_START                0x400
+
+#define SETHALTNOT                             0x10c
+
+/*** JTAG registers. ***/
+
+#define DTMCONTROL                                     0x10
+#define DTMCONTROL_DBUS_RESET          (1<<16)
+#define DTMCONTROL_IDLE                                (7<<10)
+#define DTMCONTROL_ADDRBITS                    (0xf<<4)
+#define DTMCONTROL_VERSION                     (0xf)
+
+#define DBUS                                           0x11
+#define DBUS_OP_START                          0
+#define DBUS_OP_SIZE                           2
+typedef enum {
+       DBUS_OP_NOP = 0,
+       DBUS_OP_READ = 1,
+       DBUS_OP_WRITE = 2
+} dbus_op_t;
+typedef enum {
+       DBUS_STATUS_SUCCESS = 0,
+       DBUS_STATUS_FAILED = 2,
+       DBUS_STATUS_BUSY = 3
+} dbus_status_t;
+#define DBUS_DATA_START                                2
+#define DBUS_DATA_SIZE                         34
+#define DBUS_ADDRESS_START                     36
+
+typedef enum {
+       RE_OK,
+       RE_FAIL,
+       RE_AGAIN
+} riscv_error_t;
+
+typedef enum slot {
+       SLOT0,
+       SLOT1,
+       SLOT_LAST,
+} slot_t;
+
+/*** Debug Bus registers. ***/
+
+#define DMCONTROL                              0x10
+#define DMCONTROL_INTERRUPT            (((uint64_t)1)<<33)
+#define DMCONTROL_HALTNOT              (((uint64_t)1)<<32)
+#define DMCONTROL_BUSERROR             (7<<19)
+#define DMCONTROL_SERIAL               (3<<16)
+#define DMCONTROL_AUTOINCREMENT        (1<<15)
+#define DMCONTROL_ACCESS               (7<<12)
+#define DMCONTROL_HARTID               (0x3ff<<2)
+#define DMCONTROL_NDRESET              (1<<1)
+#define DMCONTROL_FULLRESET            1
+
+#define DMINFO                                 0x11
+#define DMINFO_ABUSSIZE                        (0x7fU<<25)
+#define DMINFO_SERIALCOUNT             (0xf<<21)
+#define DMINFO_ACCESS128               (1<<20)
+#define DMINFO_ACCESS64                        (1<<19)
+#define DMINFO_ACCESS32                        (1<<18)
+#define DMINFO_ACCESS16                        (1<<17)
+#define DMINFO_ACCESS8                 (1<<16)
+#define DMINFO_DRAMSIZE                        (0x3f<<10)
+#define DMINFO_AUTHENTICATED   (1<<5)
+#define DMINFO_AUTHBUSY                        (1<<4)
+#define DMINFO_AUTHTYPE                        (3<<2)
+#define DMINFO_VERSION                 3
+
+/*** Info about the core being debugged. ***/
+
+#define DBUS_ADDRESS_UNKNOWN   0xffff
+
+#define DRAM_CACHE_SIZE                16
+
+struct trigger {
+       uint64_t address;
+       uint32_t length;
+       uint64_t mask;
+       uint64_t value;
+       bool read, write, execute;
+       int unique_id;
+};
+
+struct memory_cache_line {
+       uint32_t data;
+       bool valid;
+       bool dirty;
+};
+
+typedef struct {
+       /* Number of address bits in the dbus register. */
+       uint8_t addrbits;
+       /* Number of words in Debug RAM. */
+       unsigned int dramsize;
+       uint64_t dcsr;
+       uint64_t dpc;
+       uint64_t tselect;
+       bool tselect_dirty;
+       /* The value that mstatus actually has on the target right now. This is not
+        * the value we present to the user. That one may be stored in the
+        * reg_cache. */
+       uint64_t mstatus_actual;
+
+       struct memory_cache_line dram_cache[DRAM_CACHE_SIZE];
+
+       /* Number of run-test/idle cycles the target requests we do after each dbus
+        * access. */
+       unsigned int dtmcontrol_idle;
+
+       /* This value is incremented every time a dbus access comes back as "busy".
+        * It's used to determine how many run-test/idle cycles to feed the target
+        * in between accesses. */
+       unsigned int dbus_busy_delay;
+
+       /* This value is incremented every time we read the debug interrupt as
+        * high.  It's used to add extra run-test/idle cycles after setting debug
+        * interrupt high, so ideally we never have to perform a whole extra scan
+        * before the interrupt is cleared. */
+       unsigned int interrupt_high_delay;
+
+       bool need_strict_step;
+       bool never_halted;
+} riscv011_info_t;
+
+typedef struct {
+       bool haltnot;
+       bool interrupt;
+} bits_t;
+
+/*** Necessary prototypes. ***/
+
+static int poll_target(struct target *target, bool announce);
+static int riscv011_poll(struct target *target);
+static int get_register(struct target *target, riscv_reg_t *value, int hartid,
+               int regid);
+
+/*** Utility functions. ***/
+
+#define DEBUG_LENGTH   264
+
+static riscv011_info_t *get_info(const struct target *target)
+{
+       riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       return (riscv011_info_t *) info->version_specific;
+}
+
+static unsigned int slot_offset(const struct target *target, slot_t slot)
+{
+       riscv011_info_t *info = get_info(target);
+       switch (riscv_xlen(target)) {
+               case 32:
+                       switch (slot) {
+                               case SLOT0: return 4;
+                               case SLOT1: return 5;
+                               case SLOT_LAST: return info->dramsize-1;
+                       }
+               case 64:
+                       switch (slot) {
+                               case SLOT0: return 4;
+                               case SLOT1: return 6;
+                               case SLOT_LAST: return info->dramsize-2;
+                       }
+       }
+       LOG_ERROR("slot_offset called with xlen=%d, slot=%d",
+                       riscv_xlen(target), slot);
+       assert(0);
+       return 0; /* Silence -Werror=return-type */
+}
+
+static uint32_t load_slot(const struct target *target, unsigned int dest,
+               slot_t slot)
+{
+       unsigned int offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
+       return load(target, dest, ZERO, offset);
+}
+
+static uint32_t store_slot(const struct target *target, unsigned int src,
+               slot_t slot)
+{
+       unsigned int offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
+       return store(target, src, ZERO, offset);
+}
+
+static uint16_t dram_address(unsigned int index)
+{
+       if (index < 0x10)
+               return index;
+       else
+               return 0x40 + index - 0x10;
+}
+
+static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
+{
+       struct scan_field field;
+       uint8_t in_value[4];
+       uint8_t out_value[4];
+
+       buf_set_u32(out_value, 0, 32, out);
+
+       jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE);
+
+       field.num_bits = 32;
+       field.out_value = out_value;
+       field.in_value = in_value;
+       jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+       /* Always return to dbus. */
+       jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+       int retval = jtag_execute_queue();
+       if (retval != ERROR_OK) {
+               LOG_ERROR("failed jtag scan: %d", retval);
+               return retval;
+       }
+
+       uint32_t in = buf_get_u32(field.in_value, 0, 32);
+       LOG_DEBUG("DTMCONTROL: 0x%x -> 0x%x", out, in);
+
+       return in;
+}
+
+static uint32_t idcode_scan(struct target *target)
+{
+       struct scan_field field;
+       uint8_t in_value[4];
+
+       jtag_add_ir_scan(target->tap, &select_idcode, TAP_IDLE);
+
+       field.num_bits = 32;
+       field.out_value = NULL;
+       field.in_value = in_value;
+       jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+       int retval = jtag_execute_queue();
+       if (retval != ERROR_OK) {
+               LOG_ERROR("failed jtag scan: %d", retval);
+               return retval;
+       }
+
+       /* Always return to dbus. */
+       jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+       uint32_t in = buf_get_u32(field.in_value, 0, 32);
+       LOG_DEBUG("IDCODE: 0x0 -> 0x%x", in);
+
+       return in;
+}
+
+static void increase_dbus_busy_delay(struct target *target)
+{
+       riscv011_info_t *info = get_info(target);
+       info->dbus_busy_delay += info->dbus_busy_delay / 10 + 1;
+       LOG_DEBUG("dtmcontrol_idle=%d, dbus_busy_delay=%d, interrupt_high_delay=%d",
+                       info->dtmcontrol_idle, info->dbus_busy_delay,
+                       info->interrupt_high_delay);
+
+       dtmcontrol_scan(target, DTMCONTROL_DBUS_RESET);
+}
+
+static void increase_interrupt_high_delay(struct target *target)
+{
+       riscv011_info_t *info = get_info(target);
+       info->interrupt_high_delay += info->interrupt_high_delay / 10 + 1;
+       LOG_DEBUG("dtmcontrol_idle=%d, dbus_busy_delay=%d, interrupt_high_delay=%d",
+                       info->dtmcontrol_idle, info->dbus_busy_delay,
+                       info->interrupt_high_delay);
+}
+
+static void add_dbus_scan(const struct target *target, struct scan_field *field,
+               uint8_t *out_value, uint8_t *in_value, dbus_op_t op,
+               uint16_t address, uint64_t data)
+{
+       riscv011_info_t *info = get_info(target);
+
+       field->num_bits = info->addrbits + DBUS_OP_SIZE + DBUS_DATA_SIZE;
+       field->in_value = in_value;
+       field->out_value = out_value;
+
+       buf_set_u64(out_value, DBUS_OP_START, DBUS_OP_SIZE, op);
+       buf_set_u64(out_value, DBUS_DATA_START, DBUS_DATA_SIZE, data);
+       buf_set_u64(out_value, DBUS_ADDRESS_START, info->addrbits, address);
+
+       jtag_add_dr_scan(target->tap, 1, field, TAP_IDLE);
+
+       int idle_count = info->dtmcontrol_idle + info->dbus_busy_delay;
+       if (data & DMCONTROL_INTERRUPT)
+               idle_count += info->interrupt_high_delay;
+
+       if (idle_count)
+               jtag_add_runtest(idle_count, TAP_IDLE);
+}
+
+static void dump_field(const struct scan_field *field)
+{
+       static const char * const op_string[] = {"nop", "r", "w", "?"};
+       static const char * const status_string[] = {"+", "?", "F", "b"};
+
+       if (debug_level < LOG_LVL_DEBUG)
+               return;
+
+       uint64_t out = buf_get_u64(field->out_value, 0, field->num_bits);
+       unsigned int out_op = (out >> DBUS_OP_START) & ((1 << DBUS_OP_SIZE) - 1);
+       char out_interrupt = ((out >> DBUS_DATA_START) & DMCONTROL_INTERRUPT) ? 'i' : '.';
+       char out_haltnot = ((out >> DBUS_DATA_START) & DMCONTROL_HALTNOT) ? 'h' : '.';
+       unsigned int out_data = out >> 2;
+       unsigned int out_address = out >> DBUS_ADDRESS_START;
+       uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
+       unsigned int in_op = (in >> DBUS_OP_START) & ((1 << DBUS_OP_SIZE) - 1);
+       char in_interrupt = ((in >> DBUS_DATA_START) & DMCONTROL_INTERRUPT) ? 'i' : '.';
+       char in_haltnot = ((in >> DBUS_DATA_START) & DMCONTROL_HALTNOT) ? 'h' : '.';
+       unsigned int in_data = in >> 2;
+       unsigned int in_address = in >> DBUS_ADDRESS_START;
+
+       log_printf_lf(LOG_LVL_DEBUG,
+                       __FILE__, __LINE__, "scan",
+                       "%db %s %c%c:%08x @%02x -> %s %c%c:%08x @%02x",
+                       field->num_bits,
+                       op_string[out_op], out_interrupt, out_haltnot, out_data,
+                       out_address,
+                       status_string[in_op], in_interrupt, in_haltnot, in_data,
+                       in_address);
+}
+
+static dbus_status_t dbus_scan(struct target *target, uint16_t *address_in,
+               uint64_t *data_in, dbus_op_t op, uint16_t address_out, uint64_t data_out)
+{
+       riscv011_info_t *info = get_info(target);
+       uint8_t in[8] = {0};
+       uint8_t out[8];
+       struct scan_field field = {
+               .num_bits = info->addrbits + DBUS_OP_SIZE + DBUS_DATA_SIZE,
+               .out_value = out,
+               .in_value = in
+       };
+
+       assert(info->addrbits != 0);
+
+       buf_set_u64(out, DBUS_OP_START, DBUS_OP_SIZE, op);
+       buf_set_u64(out, DBUS_DATA_START, DBUS_DATA_SIZE, data_out);
+       buf_set_u64(out, DBUS_ADDRESS_START, info->addrbits, address_out);
+
+       /* Assume dbus is already selected. */
+       jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+       int idle_count = info->dtmcontrol_idle + info->dbus_busy_delay;
+
+       if (idle_count)
+               jtag_add_runtest(idle_count, TAP_IDLE);
+
+       int retval = jtag_execute_queue();
+       if (retval != ERROR_OK) {
+               LOG_ERROR("dbus_scan failed jtag scan");
+               return DBUS_STATUS_FAILED;
+       }
+
+       if (data_in)
+               *data_in = buf_get_u64(in, DBUS_DATA_START, DBUS_DATA_SIZE);
+
+       if (address_in)
+               *address_in = buf_get_u32(in, DBUS_ADDRESS_START, info->addrbits);
+
+       dump_field(&field);
+
+       return buf_get_u32(in, DBUS_OP_START, DBUS_OP_SIZE);
+}
+
+static uint64_t dbus_read(struct target *target, uint16_t address)
+{
+       uint64_t value;
+       dbus_status_t status;
+       uint16_t address_in;
+
+       /* If the previous read/write was to the same address, we will get the read data
+        * from the previous access.
+        * While somewhat nonintuitive, this is an efficient way to get the data.
+        */
+
+       unsigned i = 0;
+       do {
+               status = dbus_scan(target, &address_in, &value, DBUS_OP_READ, address, 0);
+               if (status == DBUS_STATUS_BUSY)
+                       increase_dbus_busy_delay(target);
+               if (status == DBUS_STATUS_FAILED) {
+                       LOG_ERROR("dbus_read(0x%x) failed!", address);
+                       return 0;
+               }
+       } while (((status == DBUS_STATUS_BUSY) || (address_in != address)) &&
+                       i++ < 256);
+
+       if (status != DBUS_STATUS_SUCCESS)
+               LOG_ERROR("failed read from 0x%x; value=0x%" PRIx64 ", status=%d\n", address, value, status);
+
+       return value;
+}
+
+static void dbus_write(struct target *target, uint16_t address, uint64_t value)
+{
+       dbus_status_t status = DBUS_STATUS_BUSY;
+       unsigned i = 0;
+       while (status == DBUS_STATUS_BUSY && i++ < 256) {
+               status = dbus_scan(target, NULL, NULL, DBUS_OP_WRITE, address, value);
+               if (status == DBUS_STATUS_BUSY)
+                       increase_dbus_busy_delay(target);
+       }
+       if (status != DBUS_STATUS_SUCCESS)
+               LOG_ERROR("failed to write 0x%" PRIx64 " to 0x%x; status=%d\n", value, address, status);
+}
+
+/*** scans "class" ***/
+
+typedef struct {
+       /* Number of scans that space is reserved for. */
+       unsigned int scan_count;
+       /* Size reserved in memory for each scan, in bytes. */
+       unsigned int scan_size;
+       unsigned int next_scan;
+       uint8_t *in;
+       uint8_t *out;
+       struct scan_field *field;
+       const struct target *target;
+} scans_t;
+
+static scans_t *scans_new(struct target *target, unsigned int scan_count)
+{
+       scans_t *scans = malloc(sizeof(scans_t));
+       scans->scan_count = scan_count;
+       /* This code also gets called before xlen is detected. */
+       if (riscv_xlen(target))
+               scans->scan_size = 2 + riscv_xlen(target) / 8;
+       else
+               scans->scan_size = 2 + 128 / 8;
+       scans->next_scan = 0;
+       scans->in = calloc(scans->scan_size, scans->scan_count);
+       scans->out = calloc(scans->scan_size, scans->scan_count);
+       scans->field = calloc(scans->scan_count, sizeof(struct scan_field));
+       scans->target = target;
+       return scans;
+}
+
+static scans_t *scans_delete(scans_t *scans)
+{
+       assert(scans);
+       free(scans->field);
+       free(scans->out);
+       free(scans->in);
+       free(scans);
+       return NULL;
+}
+
+static void scans_reset(scans_t *scans)
+{
+       scans->next_scan = 0;
+}
+
+static void scans_dump(scans_t *scans)
+{
+       for (unsigned int i = 0; i < scans->next_scan; i++)
+               dump_field(&scans->field[i]);
+}
+
+static int scans_execute(scans_t *scans)
+{
+       int retval = jtag_execute_queue();
+       if (retval != ERROR_OK) {
+               LOG_ERROR("failed jtag scan: %d", retval);
+               return retval;
+       }
+
+       scans_dump(scans);
+
+       return ERROR_OK;
+}
+
+/** Add a 32-bit dbus write to the scans structure. */
+static void scans_add_write32(scans_t *scans, uint16_t address, uint32_t data,
+               bool set_interrupt)
+{
+       const unsigned int i = scans->next_scan;
+       int data_offset = scans->scan_size * i;
+       add_dbus_scan(scans->target, &scans->field[i], scans->out + data_offset,
+                       scans->in + data_offset, DBUS_OP_WRITE, address,
+                       (set_interrupt ? DMCONTROL_INTERRUPT : 0) | DMCONTROL_HALTNOT | data);
+       scans->next_scan++;
+       assert(scans->next_scan <= scans->scan_count);
+}
+
+/** Add a 32-bit dbus write for an instruction that jumps to the beginning of
+ * debug RAM. */
+static void scans_add_write_jump(scans_t *scans, uint16_t address,
+               bool set_interrupt)
+{
+       scans_add_write32(scans, address,
+                       jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*address))),
+                       set_interrupt);
+}
+
+/** Add a 32-bit dbus write for an instruction that loads from the indicated
+ * slot. */
+static void scans_add_write_load(scans_t *scans, uint16_t address,
+               unsigned int reg, slot_t slot, bool set_interrupt)
+{
+       scans_add_write32(scans, address, load_slot(scans->target, reg, slot),
+                       set_interrupt);
+}
+
+/** Add a 32-bit dbus write for an instruction that stores to the indicated
+ * slot. */
+static void scans_add_write_store(scans_t *scans, uint16_t address,
+               unsigned int reg, slot_t slot, bool set_interrupt)
+{
+       scans_add_write32(scans, address, store_slot(scans->target, reg, slot),
+                       set_interrupt);
+}
+
+/** Add a 32-bit dbus read. */
+static void scans_add_read32(scans_t *scans, uint16_t address, bool set_interrupt)
+{
+       assert(scans->next_scan < scans->scan_count);
+       const unsigned int i = scans->next_scan;
+       int data_offset = scans->scan_size * i;
+       add_dbus_scan(scans->target, &scans->field[i], scans->out + data_offset,
+                       scans->in + data_offset, DBUS_OP_READ, address,
+                       (set_interrupt ? DMCONTROL_INTERRUPT : 0) | DMCONTROL_HALTNOT);
+       scans->next_scan++;
+}
+
+/** Add one or more scans to read the indicated slot. */
+static void scans_add_read(scans_t *scans, slot_t slot, bool set_interrupt)
+{
+       const struct target *target = scans->target;
+       switch (riscv_xlen(target)) {
+               case 32:
+                       scans_add_read32(scans, slot_offset(target, slot), set_interrupt);
+                       break;
+               case 64:
+                       scans_add_read32(scans, slot_offset(target, slot), false);
+                       scans_add_read32(scans, slot_offset(target, slot) + 1, set_interrupt);
+                       break;
+       }
+}
+
+static uint32_t scans_get_u32(scans_t *scans, unsigned int index,
+               unsigned first, unsigned num)
+{
+       return buf_get_u32(scans->in + scans->scan_size * index, first, num);
+}
+
+static uint64_t scans_get_u64(scans_t *scans, unsigned int index,
+               unsigned first, unsigned num)
+{
+       return buf_get_u64(scans->in + scans->scan_size * index, first, num);
+}
+
+/*** end of scans class ***/
+
+static uint32_t dram_read32(struct target *target, unsigned int index)
+{
+       uint16_t address = dram_address(index);
+       uint32_t value = dbus_read(target, address);
+       return value;
+}
+
+static void dram_write32(struct target *target, unsigned int index, uint32_t value,
+               bool set_interrupt)
+{
+       uint64_t dbus_value = DMCONTROL_HALTNOT | value;
+       if (set_interrupt)
+               dbus_value |= DMCONTROL_INTERRUPT;
+       dbus_write(target, dram_address(index), dbus_value);
+}
+
+/** Read the haltnot and interrupt bits. */
+static bits_t read_bits(struct target *target)
+{
+       uint64_t value;
+       dbus_status_t status;
+       uint16_t address_in;
+       riscv011_info_t *info = get_info(target);
+
+       bits_t err_result = {
+               .haltnot = 0,
+               .interrupt = 0
+       };
+
+       do {
+               unsigned i = 0;
+               do {
+                       status = dbus_scan(target, &address_in, &value, DBUS_OP_READ, 0, 0);
+                       if (status == DBUS_STATUS_BUSY) {
+                               if (address_in == (1<<info->addrbits) - 1 &&
+                                               value == (1ULL<<DBUS_DATA_SIZE) - 1) {
+                                       LOG_ERROR("TDO seems to be stuck high.");
+                                       return err_result;
+                               }
+                               increase_dbus_busy_delay(target);
+                       } else if (status == DBUS_STATUS_FAILED) {
+                               /* TODO: return an actual error */
+                               return err_result;
+                       }
+               } while (status == DBUS_STATUS_BUSY && i++ < 256);
+
+               if (i >= 256) {
+                       LOG_ERROR("Failed to read from 0x%x; status=%d", address_in, status);
+                       return err_result;
+               }
+       } while (address_in > 0x10 && address_in != DMCONTROL);
+
+       bits_t result = {
+               .haltnot = get_field(value, DMCONTROL_HALTNOT),
+               .interrupt = get_field(value, DMCONTROL_INTERRUPT)
+       };
+       return result;
+}
+
+static int wait_for_debugint_clear(struct target *target, bool ignore_first)
+{
+       time_t start = time(NULL);
+       if (ignore_first) {
+               /* Throw away the results of the first read, since they'll contain the
+                * result of the read that happened just before debugint was set.
+                * (Assuming the last scan before calling this function was one that
+                * sets debugint.) */
+               read_bits(target);
+       }
+       while (1) {
+               bits_t bits = read_bits(target);
+               if (!bits.interrupt)
+                       return ERROR_OK;
+               if (time(NULL) - start > riscv_command_timeout_sec) {
+                       LOG_ERROR("Timed out waiting for debug int to clear."
+                                 "Increase timeout with riscv set_command_timeout_sec.");
+                       return ERROR_FAIL;
+               }
+       }
+}
+
+static int dram_check32(struct target *target, unsigned int index,
+               uint32_t expected)
+{
+       uint16_t address = dram_address(index);
+       uint32_t actual = dbus_read(target, address);
+       if (expected != actual) {
+               LOG_ERROR("Wrote 0x%x to Debug RAM at %d, but read back 0x%x",
+                               expected, index, actual);
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
+static void cache_set32(struct target *target, unsigned int index, uint32_t data)
+{
+       riscv011_info_t *info = get_info(target);
+       if (info->dram_cache[index].valid &&
+                       info->dram_cache[index].data == data) {
+               /* This is already preset on the target. */
+               LOG_DEBUG("cache[0x%x] = 0x%08x: DASM(0x%x) (hit)", index, data, data);
+               return;
+       }
+       LOG_DEBUG("cache[0x%x] = 0x%08x: DASM(0x%x)", index, data, data);
+       info->dram_cache[index].data = data;
+       info->dram_cache[index].valid = true;
+       info->dram_cache[index].dirty = true;
+}
+
+static void cache_set(struct target *target, slot_t slot, uint64_t data)
+{
+       unsigned int offset = slot_offset(target, slot);
+       cache_set32(target, offset, data);
+       if (riscv_xlen(target) > 32)
+               cache_set32(target, offset + 1, data >> 32);
+}
+
+static void cache_set_jump(struct target *target, unsigned int index)
+{
+       cache_set32(target, index,
+                       jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*index))));
+}
+
+static void cache_set_load(struct target *target, unsigned int index,
+               unsigned int reg, slot_t slot)
+{
+       uint16_t offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
+       cache_set32(target, index, load(target, reg, ZERO, offset));
+}
+
+static void cache_set_store(struct target *target, unsigned int index,
+               unsigned int reg, slot_t slot)
+{
+       uint16_t offset = DEBUG_RAM_START + 4 * slot_offset(target, slot);
+       cache_set32(target, index, store(target, reg, ZERO, offset));
+}
+
+static void dump_debug_ram(struct target *target)
+{
+       for (unsigned int i = 0; i < DRAM_CACHE_SIZE; i++) {
+               uint32_t value = dram_read32(target, i);
+               LOG_ERROR("Debug RAM 0x%x: 0x%08x", i, value);
+       }
+}
+
+/* Call this if the code you just ran writes to debug RAM entries 0 through 3. */
+static void cache_invalidate(struct target *target)
+{
+       riscv011_info_t *info = get_info(target);
+       for (unsigned int i = 0; i < info->dramsize; i++) {
+               info->dram_cache[i].valid = false;
+               info->dram_cache[i].dirty = false;
+       }
+}
+
+/* Called by cache_write() after the program has run. Also call this if you're
+ * running programs without calling cache_write(). */
+static void cache_clean(struct target *target)
+{
+       riscv011_info_t *info = get_info(target);
+       for (unsigned int i = 0; i < info->dramsize; i++) {
+               if (i >= 4)
+                       info->dram_cache[i].valid = false;
+               info->dram_cache[i].dirty = false;
+       }
+}
+
+static int cache_check(struct target *target)
+{
+       riscv011_info_t *info = get_info(target);
+       int error = 0;
+
+       for (unsigned int i = 0; i < info->dramsize; i++) {
+               if (info->dram_cache[i].valid && !info->dram_cache[i].dirty) {
+                       if (dram_check32(target, i, info->dram_cache[i].data) != ERROR_OK)
+                               error++;
+               }
+       }
+
+       if (error) {
+               dump_debug_ram(target);
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+/** Write cache to the target, and optionally run the program.
+ * Then read the value at address into the cache, assuming address < 128. */
+#define CACHE_NO_READ  128
+static int cache_write(struct target *target, unsigned int address, bool run)
+{
+       LOG_DEBUG("enter");
+       riscv011_info_t *info = get_info(target);
+       scans_t *scans = scans_new(target, info->dramsize + 2);
+
+       unsigned int last = info->dramsize;
+       for (unsigned int i = 0; i < info->dramsize; i++) {
+               if (info->dram_cache[i].dirty)
+                       last = i;
+       }
+
+       if (last == info->dramsize) {
+               /* Nothing needs to be written to RAM. */
+               dbus_write(target, DMCONTROL, DMCONTROL_HALTNOT | (run ? DMCONTROL_INTERRUPT : 0));
+
+       } else {
+               for (unsigned int i = 0; i < info->dramsize; i++) {
+                       if (info->dram_cache[i].dirty) {
+                               bool set_interrupt = (i == last && run);
+                               scans_add_write32(scans, i, info->dram_cache[i].data,
+                                               set_interrupt);
+                       }
+               }
+       }
+
+       if (run || address < CACHE_NO_READ) {
+               /* Throw away the results of the first read, since it'll contain the
+                * result of the read that happened just before debugint was set. */
+               scans_add_read32(scans, address, false);
+
+               /* This scan contains the results of the read the caller requested, as
+                * well as an interrupt bit worth looking at. */
+               scans_add_read32(scans, address, false);
+       }
+
+       int retval = scans_execute(scans);
+       if (retval != ERROR_OK) {
+               scans_delete(scans);
+               LOG_ERROR("JTAG execute failed.");
+               return retval;
+       }
+
+       int errors = 0;
+       for (unsigned int i = 0; i < scans->next_scan; i++) {
+               dbus_status_t status = scans_get_u32(scans, i, DBUS_OP_START,
+                               DBUS_OP_SIZE);
+               switch (status) {
+                       case DBUS_STATUS_SUCCESS:
+                               break;
+                       case DBUS_STATUS_FAILED:
+                               LOG_ERROR("Debug RAM write failed. Hardware error?");
+                               scans_delete(scans);
+                               return ERROR_FAIL;
+                       case DBUS_STATUS_BUSY:
+                               errors++;
+                               break;
+                       default:
+                               LOG_ERROR("Got invalid bus access status: %d", status);
+                               scans_delete(scans);
+                               return ERROR_FAIL;
+               }
+       }
+
+       if (errors) {
+               increase_dbus_busy_delay(target);
+
+               /* Try again, using the slow careful code.
+                * Write all RAM, just to be extra cautious. */
+               for (unsigned int i = 0; i < info->dramsize; i++) {
+                       if (i == last && run)
+                               dram_write32(target, last, info->dram_cache[last].data, true);
+                       else
+                               dram_write32(target, i, info->dram_cache[i].data, false);
+                       info->dram_cache[i].dirty = false;
+               }
+               if (run)
+                       cache_clean(target);
+
+               if (wait_for_debugint_clear(target, true) != ERROR_OK) {
+                       LOG_ERROR("Debug interrupt didn't clear.");
+                       dump_debug_ram(target);
+                       scans_delete(scans);
+                       return ERROR_FAIL;
+               }
+
+       } else {
+               if (run) {
+                       cache_clean(target);
+               } else {
+                       for (unsigned int i = 0; i < info->dramsize; i++)
+                               info->dram_cache[i].dirty = false;
+               }
+
+               if (run || address < CACHE_NO_READ) {
+                       int interrupt = scans_get_u32(scans, scans->next_scan-1,
+                                       DBUS_DATA_START + 33, 1);
+                       if (interrupt) {
+                               increase_interrupt_high_delay(target);
+                               /* Slow path wait for it to clear. */
+                               if (wait_for_debugint_clear(target, false) != ERROR_OK) {
+                                       LOG_ERROR("Debug interrupt didn't clear.");
+                                       dump_debug_ram(target);
+                                       scans_delete(scans);
+                                       return ERROR_FAIL;
+                               }
+                       } else {
+                               /* We read a useful value in that last scan. */
+                               unsigned int read_addr = scans_get_u32(scans, scans->next_scan-1,
+                                               DBUS_ADDRESS_START, info->addrbits);
+                               if (read_addr != address) {
+                                       LOG_INFO("Got data from 0x%x but expected it from 0x%x",
+                                                       read_addr, address);
+                               }
+                               info->dram_cache[read_addr].data =
+                                       scans_get_u32(scans, scans->next_scan-1, DBUS_DATA_START, 32);
+                               info->dram_cache[read_addr].valid = true;
+                       }
+               }
+       }
+
+       scans_delete(scans);
+       LOG_DEBUG("exit");
+
+       return ERROR_OK;
+}
+
+static uint32_t cache_get32(struct target *target, unsigned int address)
+{
+       riscv011_info_t *info = get_info(target);
+       if (!info->dram_cache[address].valid) {
+               info->dram_cache[address].data = dram_read32(target, address);
+               info->dram_cache[address].valid = true;
+       }
+       return info->dram_cache[address].data;
+}
+
+static uint64_t cache_get(struct target *target, slot_t slot)
+{
+       unsigned int offset = slot_offset(target, slot);
+       uint64_t value = cache_get32(target, offset);
+       if (riscv_xlen(target) > 32)
+               value |= ((uint64_t) cache_get32(target, offset + 1)) << 32;
+       return value;
+}
+
+/* Write instruction that jumps from the specified word in Debug RAM to resume
+ * in Debug ROM. */
+static void dram_write_jump(struct target *target, unsigned int index,
+               bool set_interrupt)
+{
+       dram_write32(target, index,
+                       jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*index))),
+                       set_interrupt);
+}
+
+static int wait_for_state(struct target *target, enum target_state state)
+{
+       time_t start = time(NULL);
+       while (1) {
+               int result = riscv011_poll(target);
+               if (result != ERROR_OK)
+                       return result;
+               if (target->state == state)
+                       return ERROR_OK;
+               if (time(NULL) - start > riscv_command_timeout_sec) {
+                       LOG_ERROR("Timed out waiting for state %d. "
+                                 "Increase timeout with riscv set_command_timeout_sec.", state);
+                       return ERROR_FAIL;
+               }
+       }
+}
+
+static int read_csr(struct target *target, uint64_t *value, uint32_t csr)
+{
+       riscv011_info_t *info = get_info(target);
+       cache_set32(target, 0, csrr(S0, csr));
+       cache_set_store(target, 1, S0, SLOT0);
+       cache_set_jump(target, 2);
+       if (cache_write(target, 4, true) != ERROR_OK)
+               return ERROR_FAIL;
+       *value = cache_get(target, SLOT0);
+       LOG_DEBUG("csr 0x%x = 0x%" PRIx64, csr, *value);
+
+       uint32_t exception = cache_get32(target, info->dramsize-1);
+       if (exception) {
+               LOG_WARNING("Got exception 0x%x when reading %s", exception,
+                               gdb_regno_name(GDB_REGNO_CSR0 + csr));
+               *value = ~0;
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+static int write_csr(struct target *target, uint32_t csr, uint64_t value)
+{
+       LOG_DEBUG("csr 0x%x <- 0x%" PRIx64, csr, value);
+       cache_set_load(target, 0, S0, SLOT0);
+       cache_set32(target, 1, csrw(S0, csr));
+       cache_set_jump(target, 2);
+       cache_set(target, SLOT0, value);
+       if (cache_write(target, 4, true) != ERROR_OK)
+               return ERROR_FAIL;
+
+       return ERROR_OK;
+}
+
+static int write_gpr(struct target *target, unsigned int gpr, uint64_t value)
+{
+       cache_set_load(target, 0, gpr, SLOT0);
+       cache_set_jump(target, 1);
+       cache_set(target, SLOT0, value);
+       if (cache_write(target, 4, true) != ERROR_OK)
+               return ERROR_FAIL;
+       return ERROR_OK;
+}
+
+static int maybe_read_tselect(struct target *target)
+{
+       riscv011_info_t *info = get_info(target);
+
+       if (info->tselect_dirty) {
+               int result = read_csr(target, &info->tselect, CSR_TSELECT);
+               if (result != ERROR_OK)
+                       return result;
+               info->tselect_dirty = false;
+       }
+
+       return ERROR_OK;
+}
+
+static int maybe_write_tselect(struct target *target)
+{
+       riscv011_info_t *info = get_info(target);
+
+       if (!info->tselect_dirty) {
+               int result = write_csr(target, CSR_TSELECT, info->tselect);
+               if (result != ERROR_OK)
+                       return result;
+               info->tselect_dirty = true;
+       }
+
+       return ERROR_OK;
+}
+
+static int execute_resume(struct target *target, bool step)
+{
+       riscv011_info_t *info = get_info(target);
+
+       LOG_DEBUG("step=%d", step);
+
+       maybe_write_tselect(target);
+
+       /* TODO: check if dpc is dirty (which also is true if an exception was hit
+        * at any time) */
+       cache_set_load(target, 0, S0, SLOT0);
+       cache_set32(target, 1, csrw(S0, CSR_DPC));
+       cache_set_jump(target, 2);
+       cache_set(target, SLOT0, info->dpc);
+       if (cache_write(target, 4, true) != ERROR_OK)
+               return ERROR_FAIL;
+
+       struct reg *mstatus_reg = &target->reg_cache->reg_list[GDB_REGNO_MSTATUS];
+       if (mstatus_reg->valid) {
+               uint64_t mstatus_user = buf_get_u64(mstatus_reg->value, 0, riscv_xlen(target));
+               if (mstatus_user != info->mstatus_actual) {
+                       cache_set_load(target, 0, S0, SLOT0);
+                       cache_set32(target, 1, csrw(S0, CSR_MSTATUS));
+                       cache_set_jump(target, 2);
+                       cache_set(target, SLOT0, mstatus_user);
+                       if (cache_write(target, 4, true) != ERROR_OK)
+                               return ERROR_FAIL;
+               }
+       }
+
+       info->dcsr |= DCSR_EBREAKM | DCSR_EBREAKH | DCSR_EBREAKS | DCSR_EBREAKU;
+       info->dcsr &= ~DCSR_HALT;
+
+       if (step)
+               info->dcsr |= DCSR_STEP;
+       else
+               info->dcsr &= ~DCSR_STEP;
+
+       dram_write32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16), false);
+       dram_write32(target, 1, csrw(S0, CSR_DCSR), false);
+       dram_write32(target, 2, fence_i(), false);
+       dram_write_jump(target, 3, false);
+
+       /* Write DCSR value, set interrupt and clear haltnot. */
+       uint64_t dbus_value = DMCONTROL_INTERRUPT | info->dcsr;
+       dbus_write(target, dram_address(4), dbus_value);
+
+       cache_invalidate(target);
+
+       if (wait_for_debugint_clear(target, true) != ERROR_OK) {
+               LOG_ERROR("Debug interrupt didn't clear.");
+               return ERROR_FAIL;
+       }
+
+       target->state = TARGET_RUNNING;
+       register_cache_invalidate(target->reg_cache);
+
+       return ERROR_OK;
+}
+
+/* Execute a step, and wait for reentry into Debug Mode. */
+static int full_step(struct target *target, bool announce)
+{
+       int result = execute_resume(target, true);
+       if (result != ERROR_OK)
+               return result;
+       time_t start = time(NULL);
+       while (1) {
+               result = poll_target(target, announce);
+               if (result != ERROR_OK)
+                       return result;
+               if (target->state != TARGET_DEBUG_RUNNING)
+                       break;
+               if (time(NULL) - start > riscv_command_timeout_sec) {
+                       LOG_ERROR("Timed out waiting for step to complete."
+                                       "Increase timeout with riscv set_command_timeout_sec");
+                       return ERROR_FAIL;
+               }
+       }
+       return ERROR_OK;
+}
+
+static int resume(struct target *target, int debug_execution, bool step)
+{
+       if (debug_execution) {
+               LOG_ERROR("TODO: debug_execution is true");
+               return ERROR_FAIL;
+       }
+
+       return execute_resume(target, step);
+}
+
+static uint64_t reg_cache_get(struct target *target, unsigned int number)
+{
+       struct reg *r = &target->reg_cache->reg_list[number];
+       if (!r->valid) {
+               LOG_ERROR("Register cache entry for %d is invalid!", number);
+               assert(r->valid);
+       }
+       uint64_t value = buf_get_u64(r->value, 0, r->size);
+       LOG_DEBUG("%s = 0x%" PRIx64, r->name, value);
+       return value;
+}
+
+static void reg_cache_set(struct target *target, unsigned int number,
+               uint64_t value)
+{
+       struct reg *r = &target->reg_cache->reg_list[number];
+       LOG_DEBUG("%s <= 0x%" PRIx64, r->name, value);
+       r->valid = true;
+       buf_set_u64(r->value, 0, r->size, value);
+}
+
+static int update_mstatus_actual(struct target *target)
+{
+       struct reg *mstatus_reg = &target->reg_cache->reg_list[GDB_REGNO_MSTATUS];
+       if (mstatus_reg->valid) {
+               /* We previously made it valid. */
+               return ERROR_OK;
+       }
+
+       /* Force reading the register. In that process mstatus_actual will be
+        * updated. */
+       riscv_reg_t mstatus;
+       return get_register(target, &mstatus, 0, GDB_REGNO_MSTATUS);
+}
+
+/*** OpenOCD target functions. ***/
+
+static int register_read(struct target *target, riscv_reg_t *value, int regnum)
+{
+       riscv011_info_t *info = get_info(target);
+       if (regnum >= GDB_REGNO_CSR0 && regnum <= GDB_REGNO_CSR4095) {
+               cache_set32(target, 0, csrr(S0, regnum - GDB_REGNO_CSR0));
+               cache_set_store(target, 1, S0, SLOT0);
+               cache_set_jump(target, 2);
+       } else {
+               LOG_ERROR("Don't know how to read register %d", regnum);
+               return ERROR_FAIL;
+       }
+
+       if (cache_write(target, 4, true) != ERROR_OK)
+               return ERROR_FAIL;
+
+       uint32_t exception = cache_get32(target, info->dramsize-1);
+       if (exception) {
+               LOG_WARNING("Got exception 0x%x when reading %s", exception, gdb_regno_name(regnum));
+               *value = ~0;
+               return ERROR_FAIL;
+       }
+
+       *value = cache_get(target, SLOT0);
+       LOG_DEBUG("reg[%d]=0x%" PRIx64, regnum, *value);
+
+       if (regnum == GDB_REGNO_MSTATUS)
+               info->mstatus_actual = *value;
+
+       return ERROR_OK;
+}
+
+/* Write the register. No caching or games. */
+static int register_write(struct target *target, unsigned int number,
+               uint64_t value)
+{
+       riscv011_info_t *info = get_info(target);
+
+       maybe_write_tselect(target);
+
+       if (number == S0) {
+               cache_set_load(target, 0, S0, SLOT0);
+               cache_set32(target, 1, csrw(S0, CSR_DSCRATCH));
+               cache_set_jump(target, 2);
+       } else if (number == S1) {
+               cache_set_load(target, 0, S0, SLOT0);
+               cache_set_store(target, 1, S0, SLOT_LAST);
+               cache_set_jump(target, 2);
+       } else if (number <= GDB_REGNO_XPR31) {
+               cache_set_load(target, 0, number - GDB_REGNO_ZERO, SLOT0);
+               cache_set_jump(target, 1);
+       } else if (number == GDB_REGNO_PC) {
+               info->dpc = value;
+               return ERROR_OK;
+       } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+               int result = update_mstatus_actual(target);
+               if (result != ERROR_OK)
+                       return result;
+               unsigned i = 0;
+               if ((info->mstatus_actual & MSTATUS_FS) == 0) {
+                       info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1);
+                       cache_set_load(target, i++, S0, SLOT1);
+                       cache_set32(target, i++, csrw(S0, CSR_MSTATUS));
+                       cache_set(target, SLOT1, info->mstatus_actual);
+               }
+
+               if (riscv_xlen(target) == 32)
+                       cache_set32(target, i++, flw(number - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
+               else
+                       cache_set32(target, i++, fld(number - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
+               cache_set_jump(target, i++);
+       } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+               cache_set_load(target, 0, S0, SLOT0);
+               cache_set32(target, 1, csrw(S0, number - GDB_REGNO_CSR0));
+               cache_set_jump(target, 2);
+
+               if (number == GDB_REGNO_MSTATUS)
+                       info->mstatus_actual = value;
+       } else if (number == GDB_REGNO_PRIV) {
+               info->dcsr = set_field(info->dcsr, DCSR_PRV, value);
+               return ERROR_OK;
+       } else {
+               LOG_ERROR("Don't know how to write register %d", number);
+               return ERROR_FAIL;
+       }
+
+       cache_set(target, SLOT0, value);
+       if (cache_write(target, info->dramsize - 1, true) != ERROR_OK)
+               return ERROR_FAIL;
+
+       uint32_t exception = cache_get32(target, info->dramsize-1);
+       if (exception) {
+               LOG_WARNING("Got exception 0x%x when writing %s", exception,
+                               gdb_regno_name(number));
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+static int get_register(struct target *target, riscv_reg_t *value, int hartid,
+               int regid)
+{
+       assert(hartid == 0);
+       riscv011_info_t *info = get_info(target);
+
+       maybe_write_tselect(target);
+
+       if (regid <= GDB_REGNO_XPR31) {
+               *value = reg_cache_get(target, regid);
+       } else if (regid == GDB_REGNO_PC) {
+               *value = info->dpc;
+       } else if (regid >= GDB_REGNO_FPR0 && regid <= GDB_REGNO_FPR31) {
+               int result = update_mstatus_actual(target);
+               if (result != ERROR_OK)
+                       return result;
+               unsigned i = 0;
+               if ((info->mstatus_actual & MSTATUS_FS) == 0) {
+                       info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1);
+                       cache_set_load(target, i++, S0, SLOT1);
+                       cache_set32(target, i++, csrw(S0, CSR_MSTATUS));
+                       cache_set(target, SLOT1, info->mstatus_actual);
+               }
+
+               if (riscv_xlen(target) == 32)
+                       cache_set32(target, i++, fsw(regid - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
+               else
+                       cache_set32(target, i++, fsd(regid - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
+               cache_set_jump(target, i++);
+
+               if (cache_write(target, 4, true) != ERROR_OK)
+                       return ERROR_FAIL;
+       } else if (regid == GDB_REGNO_PRIV) {
+               *value = get_field(info->dcsr, DCSR_PRV);
+       } else {
+               int result = register_read(target, value, regid);
+               if (result != ERROR_OK)
+                       return result;
+       }
+
+       if (regid == GDB_REGNO_MSTATUS)
+               target->reg_cache->reg_list[regid].valid = true;
+
+       return ERROR_OK;
+}
+
+static int set_register(struct target *target, int hartid, int regid,
+               uint64_t value)
+{
+       assert(hartid == 0);
+       return register_write(target, regid, value);
+}
+
+static int halt(struct target *target)
+{
+       LOG_DEBUG("riscv_halt()");
+       jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+       cache_set32(target, 0, csrsi(CSR_DCSR, DCSR_HALT));
+       cache_set32(target, 1, csrr(S0, CSR_MHARTID));
+       cache_set32(target, 2, sw(S0, ZERO, SETHALTNOT));
+       cache_set_jump(target, 3);
+
+       if (cache_write(target, 4, true) != ERROR_OK) {
+               LOG_ERROR("cache_write() failed.");
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+static int init_target(struct command_context *cmd_ctx,
+               struct target *target)
+{
+       LOG_DEBUG("init");
+       riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
+       generic_info->get_register = get_register;
+       generic_info->set_register = set_register;
+
+       generic_info->version_specific = calloc(1, sizeof(riscv011_info_t));
+       if (!generic_info->version_specific)
+               return ERROR_FAIL;
+
+       /* Assume 32-bit until we discover the real value in examine(). */
+       generic_info->xlen[0] = 32;
+       riscv_init_registers(target);
+
+       return ERROR_OK;
+}
+
+static void deinit_target(struct target *target)
+{
+       LOG_DEBUG("riscv_deinit_target()");
+       riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       free(info->version_specific);
+       info->version_specific = NULL;
+}
+
+static int strict_step(struct target *target, bool announce)
+{
+       riscv011_info_t *info = get_info(target);
+
+       LOG_DEBUG("enter");
+
+       struct breakpoint *breakpoint = target->breakpoints;
+       while (breakpoint) {
+               riscv_remove_breakpoint(target, breakpoint);
+               breakpoint = breakpoint->next;
+       }
+
+       struct watchpoint *watchpoint = target->watchpoints;
+       while (watchpoint) {
+               riscv_remove_watchpoint(target, watchpoint);
+               watchpoint = watchpoint->next;
+       }
+
+       int result = full_step(target, announce);
+       if (result != ERROR_OK)
+               return result;
+
+       breakpoint = target->breakpoints;
+       while (breakpoint) {
+               riscv_add_breakpoint(target, breakpoint);
+               breakpoint = breakpoint->next;
+       }
+
+       watchpoint = target->watchpoints;
+       while (watchpoint) {
+               riscv_add_watchpoint(target, watchpoint);
+               watchpoint = watchpoint->next;
+       }
+
+       info->need_strict_step = false;
+
+       return ERROR_OK;
+}
+
+static int step(struct target *target, int current, target_addr_t address,
+               int handle_breakpoints)
+{
+       riscv011_info_t *info = get_info(target);
+
+       jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+       if (!current) {
+               if (riscv_xlen(target) > 32) {
+                       LOG_WARNING("Asked to resume at 32-bit PC on %d-bit target.",
+                                       riscv_xlen(target));
+               }
+               int result = register_write(target, GDB_REGNO_PC, address);
+               if (result != ERROR_OK)
+                       return result;
+       }
+
+       if (info->need_strict_step || handle_breakpoints) {
+               int result = strict_step(target, true);
+               if (result != ERROR_OK)
+                       return result;
+       } else {
+               return resume(target, 0, true);
+       }
+
+       return ERROR_OK;
+}
+
+static int examine(struct target *target)
+{
+       /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
+
+       uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
+       LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
+       LOG_DEBUG("  addrbits=%d", get_field(dtmcontrol, DTMCONTROL_ADDRBITS));
+       LOG_DEBUG("  version=%d", get_field(dtmcontrol, DTMCONTROL_VERSION));
+       LOG_DEBUG("  idle=%d", get_field(dtmcontrol, DTMCONTROL_IDLE));
+       if (dtmcontrol == 0) {
+               LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power.");
+               return ERROR_FAIL;
+       }
+       if (get_field(dtmcontrol, DTMCONTROL_VERSION) != 0) {
+               LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)",
+                               get_field(dtmcontrol, DTMCONTROL_VERSION), dtmcontrol);
+               return ERROR_FAIL;
+       }
+
+       RISCV_INFO(r);
+       r->hart_count = 1;
+
+       riscv011_info_t *info = get_info(target);
+       info->addrbits = get_field(dtmcontrol, DTMCONTROL_ADDRBITS);
+       info->dtmcontrol_idle = get_field(dtmcontrol, DTMCONTROL_IDLE);
+       if (info->dtmcontrol_idle == 0) {
+               /* Some old SiFive cores don't set idle but need it to be 1. */
+               uint32_t idcode = idcode_scan(target);
+               if (idcode == 0x10e31913)
+                       info->dtmcontrol_idle = 1;
+       }
+
+       uint32_t dminfo = dbus_read(target, DMINFO);
+       LOG_DEBUG("dminfo: 0x%08x", dminfo);
+       LOG_DEBUG("  abussize=0x%x", get_field(dminfo, DMINFO_ABUSSIZE));
+       LOG_DEBUG("  serialcount=0x%x", get_field(dminfo, DMINFO_SERIALCOUNT));
+       LOG_DEBUG("  access128=%d", get_field(dminfo, DMINFO_ACCESS128));
+       LOG_DEBUG("  access64=%d", get_field(dminfo, DMINFO_ACCESS64));
+       LOG_DEBUG("  access32=%d", get_field(dminfo, DMINFO_ACCESS32));
+       LOG_DEBUG("  access16=%d", get_field(dminfo, DMINFO_ACCESS16));
+       LOG_DEBUG("  access8=%d", get_field(dminfo, DMINFO_ACCESS8));
+       LOG_DEBUG("  dramsize=0x%x", get_field(dminfo, DMINFO_DRAMSIZE));
+       LOG_DEBUG("  authenticated=0x%x", get_field(dminfo, DMINFO_AUTHENTICATED));
+       LOG_DEBUG("  authbusy=0x%x", get_field(dminfo, DMINFO_AUTHBUSY));
+       LOG_DEBUG("  authtype=0x%x", get_field(dminfo, DMINFO_AUTHTYPE));
+       LOG_DEBUG("  version=0x%x", get_field(dminfo, DMINFO_VERSION));
+
+       if (get_field(dminfo, DMINFO_VERSION) != 1) {
+               LOG_ERROR("OpenOCD only supports Debug Module version 1, not %d "
+                               "(dminfo=0x%x)", get_field(dminfo, DMINFO_VERSION), dminfo);
+               return ERROR_FAIL;
+       }
+
+       info->dramsize = get_field(dminfo, DMINFO_DRAMSIZE) + 1;
+
+       if (get_field(dminfo, DMINFO_AUTHTYPE) != 0) {
+               LOG_ERROR("Authentication required by RISC-V core but not "
+                               "supported by OpenOCD. dminfo=0x%x", dminfo);
+               return ERROR_FAIL;
+       }
+
+       /* Pretend this is a 32-bit system until we have found out the true value. */
+       r->xlen[0] = 32;
+
+       /* Figure out XLEN, and test writing all of Debug RAM while we're at it. */
+       cache_set32(target, 0, xori(S1, ZERO, -1));
+       /* 0xffffffff  0xffffffff:ffffffff  0xffffffff:ffffffff:ffffffff:ffffffff */
+       cache_set32(target, 1, srli(S1, S1, 31));
+       /* 0x00000001  0x00000001:ffffffff  0x00000001:ffffffff:ffffffff:ffffffff */
+       cache_set32(target, 2, sw(S1, ZERO, DEBUG_RAM_START));
+       cache_set32(target, 3, srli(S1, S1, 31));
+       /* 0x00000000  0x00000000:00000003  0x00000000:00000003:ffffffff:ffffffff */
+       cache_set32(target, 4, sw(S1, ZERO, DEBUG_RAM_START + 4));
+       cache_set_jump(target, 5);
+       for (unsigned i = 6; i < info->dramsize; i++)
+               cache_set32(target, i, i * 0x01020304);
+
+       cache_write(target, 0, false);
+
+       /* Check that we can actually read/write dram. */
+       if (cache_check(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       cache_write(target, 0, true);
+       cache_invalidate(target);
+
+       uint32_t word0 = cache_get32(target, 0);
+       uint32_t word1 = cache_get32(target, 1);
+       riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
+       if (word0 == 1 && word1 == 0) {
+               generic_info->xlen[0] = 32;
+       } else if (word0 == 0xffffffff && word1 == 3) {
+               generic_info->xlen[0] = 64;
+       } else if (word0 == 0xffffffff && word1 == 0xffffffff) {
+               generic_info->xlen[0] = 128;
+       } else {
+               uint32_t exception = cache_get32(target, info->dramsize-1);
+               LOG_ERROR("Failed to discover xlen; word0=0x%x, word1=0x%x, exception=0x%x",
+                               word0, word1, exception);
+               dump_debug_ram(target);
+               return ERROR_FAIL;
+       }
+       LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target));
+
+       if (read_csr(target, &r->misa[0], CSR_MISA) != ERROR_OK) {
+               const unsigned old_csr_misa = 0xf10;
+               LOG_WARNING("Failed to read misa at 0x%x; trying 0x%x.", CSR_MISA,
+                               old_csr_misa);
+               if (read_csr(target, &r->misa[0], old_csr_misa) != ERROR_OK) {
+                       /* Maybe this is an old core that still has $misa at the old
+                        * address. */
+                       LOG_ERROR("Failed to read misa at 0x%x.", old_csr_misa);
+                       return ERROR_FAIL;
+               }
+       }
+
+       /* Update register list to match discovered XLEN/supported extensions. */
+       riscv_init_registers(target);
+
+       info->never_halted = true;
+
+       int result = riscv011_poll(target);
+       if (result != ERROR_OK)
+               return result;
+
+       target_set_examined(target);
+       riscv_set_current_hartid(target, 0);
+       for (size_t i = 0; i < 32; ++i)
+               reg_cache_set(target, i, -1);
+       LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64,
+                       riscv_xlen(target), r->misa[0]);
+
+       return ERROR_OK;
+}
+
+static riscv_error_t handle_halt_routine(struct target *target)
+{
+       riscv011_info_t *info = get_info(target);
+
+       scans_t *scans = scans_new(target, 256);
+
+       /* Read all GPRs as fast as we can, because gdb is going to ask for them
+        * anyway. Reading them one at a time is much slower. */
+
+       /* Write the jump back to address 1. */
+       scans_add_write_jump(scans, 1, false);
+       for (int reg = 1; reg < 32; reg++) {
+               if (reg == S0 || reg == S1)
+                       continue;
+
+               /* Write store instruction. */
+               scans_add_write_store(scans, 0, reg, SLOT0, true);
+
+               /* Read value. */
+               scans_add_read(scans, SLOT0, false);
+       }
+
+       /* Write store of s0 at index 1. */
+       scans_add_write_store(scans, 1, S0, SLOT0, false);
+       /* Write jump at index 2. */
+       scans_add_write_jump(scans, 2, false);
+
+       /* Read S1 from debug RAM */
+       scans_add_write_load(scans, 0, S0, SLOT_LAST, true);
+       /* Read value. */
+       scans_add_read(scans, SLOT0, false);
+
+       /* Read S0 from dscratch */
+       unsigned int csr[] = {CSR_DSCRATCH, CSR_DPC, CSR_DCSR};
+       for (unsigned int i = 0; i < DIM(csr); i++) {
+               scans_add_write32(scans, 0, csrr(S0, csr[i]), true);
+               scans_add_read(scans, SLOT0, false);
+       }
+
+       /* Final read to get the last value out. */
+       scans_add_read32(scans, 4, false);
+
+       int retval = scans_execute(scans);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("JTAG execute failed: %d", retval);
+               goto error;
+       }
+
+       unsigned int dbus_busy = 0;
+       unsigned int interrupt_set = 0;
+       unsigned result = 0;
+       uint64_t value = 0;
+       reg_cache_set(target, 0, 0);
+       /* The first scan result is the result from something old we don't care
+        * about. */
+       for (unsigned int i = 1; i < scans->next_scan && dbus_busy == 0; i++) {
+               dbus_status_t status = scans_get_u32(scans, i, DBUS_OP_START,
+                               DBUS_OP_SIZE);
+               uint64_t data = scans_get_u64(scans, i, DBUS_DATA_START, DBUS_DATA_SIZE);
+               uint32_t address = scans_get_u32(scans, i, DBUS_ADDRESS_START,
+                               info->addrbits);
+               switch (status) {
+                       case DBUS_STATUS_SUCCESS:
+                               break;
+                       case DBUS_STATUS_FAILED:
+                               LOG_ERROR("Debug access failed. Hardware error?");
+                               goto error;
+                       case DBUS_STATUS_BUSY:
+                               dbus_busy++;
+                               break;
+                       default:
+                               LOG_ERROR("Got invalid bus access status: %d", status);
+                               return ERROR_FAIL;
+               }
+               if (data & DMCONTROL_INTERRUPT) {
+                       interrupt_set++;
+                       break;
+               }
+               if (address == 4 || address == 5) {
+                       unsigned int reg;
+                       switch (result) {
+                               case 0:
+                                       reg = 1;
+                                       break;
+                               case 1:
+                                       reg = 2;
+                                       break;
+                               case 2:
+                                       reg = 3;
+                                       break;
+                               case 3:
+                                       reg = 4;
+                                       break;
+                               case 4:
+                                       reg = 5;
+                                       break;
+                               case 5:
+                                       reg = 6;
+                                       break;
+                               case 6:
+                                       reg = 7;
+                                       break;
+                                       /* S0 */
+                                       /* S1 */
+                               case 7:
+                                       reg = 10;
+                                       break;
+                               case 8:
+                                       reg = 11;
+                                       break;
+                               case 9:
+                                       reg = 12;
+                                       break;
+                               case 10:
+                                       reg = 13;
+                                       break;
+                               case 11:
+                                       reg = 14;
+                                       break;
+                               case 12:
+                                       reg = 15;
+                                       break;
+                               case 13:
+                                       reg = 16;
+                                       break;
+                               case 14:
+                                       reg = 17;
+                                       break;
+                               case 15:
+                                       reg = 18;
+                                       break;
+                               case 16:
+                                       reg = 19;
+                                       break;
+                               case 17:
+                                       reg = 20;
+                                       break;
+                               case 18:
+                                       reg = 21;
+                                       break;
+                               case 19:
+                                       reg = 22;
+                                       break;
+                               case 20:
+                                       reg = 23;
+                                       break;
+                               case 21:
+                                       reg = 24;
+                                       break;
+                               case 22:
+                                       reg = 25;
+                                       break;
+                               case 23:
+                                       reg = 26;
+                                       break;
+                               case 24:
+                                       reg = 27;
+                                       break;
+                               case 25:
+                                       reg = 28;
+                                       break;
+                               case 26:
+                                       reg = 29;
+                                       break;
+                               case 27:
+                                       reg = 30;
+                                       break;
+                               case 28:
+                                       reg = 31;
+                                       break;
+                               case 29:
+                                       reg = S1;
+                                       break;
+                               case 30:
+                                       reg = S0;
+                                       break;
+                               case 31:
+                                       reg = CSR_DPC;
+                                       break;
+                               case 32:
+                                       reg = CSR_DCSR;
+                                       break;
+                               default:
+                                       assert(0);
+                       }
+                       if (riscv_xlen(target) == 32) {
+                               reg_cache_set(target, reg, data & 0xffffffff);
+                               result++;
+                       } else if (riscv_xlen(target) == 64) {
+                               if (address == 4) {
+                                       value = data & 0xffffffff;
+                               } else if (address == 5) {
+                                       reg_cache_set(target, reg, ((data & 0xffffffff) << 32) | value);
+                                       value = 0;
+                                       result++;
+                               }
+                       }
+               }
+       }
+
+       if (dbus_busy) {
+               increase_dbus_busy_delay(target);
+               return RE_AGAIN;
+       }
+       if (interrupt_set) {
+               increase_interrupt_high_delay(target);
+               return RE_AGAIN;
+       }
+
+       /* TODO: get rid of those 2 variables and talk to the cache directly. */
+       info->dpc = reg_cache_get(target, CSR_DPC);
+       info->dcsr = reg_cache_get(target, CSR_DCSR);
+
+       scans_delete(scans);
+
+       cache_invalidate(target);
+
+       return RE_OK;
+
+error:
+       scans_delete(scans);
+       return RE_FAIL;
+}
+
+static int handle_halt(struct target *target, bool announce)
+{
+       riscv011_info_t *info = get_info(target);
+       target->state = TARGET_HALTED;
+
+       riscv_error_t re;
+       do {
+               re = handle_halt_routine(target);
+       } while (re == RE_AGAIN);
+       if (re != RE_OK) {
+               LOG_ERROR("handle_halt_routine failed");
+               return ERROR_FAIL;
+       }
+
+       int cause = get_field(info->dcsr, DCSR_CAUSE);
+       switch (cause) {
+               case DCSR_CAUSE_SWBP:
+                       target->debug_reason = DBG_REASON_BREAKPOINT;
+                       break;
+               case DCSR_CAUSE_HWBP:
+                       target->debug_reason = DBG_REASON_WPTANDBKPT;
+                       /* If we halted because of a data trigger, gdb doesn't know to do
+                        * the disable-breakpoints-step-enable-breakpoints dance. */
+                       info->need_strict_step = true;
+                       break;
+               case DCSR_CAUSE_DEBUGINT:
+                       target->debug_reason = DBG_REASON_DBGRQ;
+                       break;
+               case DCSR_CAUSE_STEP:
+                       target->debug_reason = DBG_REASON_SINGLESTEP;
+                       break;
+               case DCSR_CAUSE_HALT:
+               default:
+                       LOG_ERROR("Invalid halt cause %d in DCSR (0x%" PRIx64 ")",
+                                       cause, info->dcsr);
+       }
+
+       if (info->never_halted) {
+               info->never_halted = false;
+
+               int result = maybe_read_tselect(target);
+               if (result != ERROR_OK)
+                       return result;
+               riscv_enumerate_triggers(target);
+       }
+
+       if (target->debug_reason == DBG_REASON_BREAKPOINT) {
+               int retval;
+               if (riscv_semihosting(target, &retval) != 0)
+                       return retval;
+       }
+
+       if (announce)
+               target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+
+       const char *cause_string[] = {
+               "none",
+               "software breakpoint",
+               "hardware trigger",
+               "debug interrupt",
+               "step",
+               "halt"
+       };
+       /* This is logged to the user so that gdb will show it when a user types
+        * 'monitor reset init'. At that time gdb appears to have the pc cached
+        * still so if a user manually inspects the pc it will still have the old
+        * value. */
+       LOG_USER("halted at 0x%" PRIx64 " due to %s", info->dpc, cause_string[cause]);
+
+       return ERROR_OK;
+}
+
+static int poll_target(struct target *target, bool announce)
+{
+       jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+       /* Inhibit debug logging during poll(), which isn't usually interesting and
+        * just fills up the screen/logs with clutter. */
+       int old_debug_level = debug_level;
+       if (debug_level >= LOG_LVL_DEBUG)
+               debug_level = LOG_LVL_INFO;
+       bits_t bits = read_bits(target);
+       debug_level = old_debug_level;
+
+       if (bits.haltnot && bits.interrupt) {
+               target->state = TARGET_DEBUG_RUNNING;
+               LOG_DEBUG("debug running");
+       } else if (bits.haltnot && !bits.interrupt) {
+               if (target->state != TARGET_HALTED)
+                       return handle_halt(target, announce);
+       } else if (!bits.haltnot && bits.interrupt) {
+               /* Target is halting. There is no state for that, so don't change anything. */
+               LOG_DEBUG("halting");
+       } else if (!bits.haltnot && !bits.interrupt) {
+               target->state = TARGET_RUNNING;
+       }
+
+       return ERROR_OK;
+}
+
+static int riscv011_poll(struct target *target)
+{
+       return poll_target(target, true);
+}
+
+static int riscv011_resume(struct target *target, int current,
+               target_addr_t address, int handle_breakpoints, int debug_execution)
+{
+       riscv011_info_t *info = get_info(target);
+
+       jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+       if (!current) {
+               if (riscv_xlen(target) > 32) {
+                       LOG_WARNING("Asked to resume at 32-bit PC on %d-bit target.",
+                                       riscv_xlen(target));
+               }
+               int result = register_write(target, GDB_REGNO_PC, address);
+               if (result != ERROR_OK)
+                       return result;
+       }
+
+       if (info->need_strict_step || handle_breakpoints) {
+               int result = strict_step(target, false);
+               if (result != ERROR_OK)
+                       return result;
+       }
+
+       return resume(target, debug_execution, false);
+}
+
+static int assert_reset(struct target *target)
+{
+       riscv011_info_t *info = get_info(target);
+       /* TODO: Maybe what I implemented here is more like soft_reset_halt()? */
+
+       jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+       /* The only assumption we can make is that the TAP was reset. */
+       if (wait_for_debugint_clear(target, true) != ERROR_OK) {
+               LOG_ERROR("Debug interrupt didn't clear.");
+               return ERROR_FAIL;
+       }
+
+       /* Not sure what we should do when there are multiple cores.
+        * Here just reset the single hart we're talking to. */
+       info->dcsr |= DCSR_EBREAKM | DCSR_EBREAKH | DCSR_EBREAKS |
+               DCSR_EBREAKU | DCSR_HALT;
+       if (target->reset_halt)
+               info->dcsr |= DCSR_NDRESET;
+       else
+               info->dcsr |= DCSR_FULLRESET;
+       dram_write32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16), false);
+       dram_write32(target, 1, csrw(S0, CSR_DCSR), false);
+       /* We shouldn't actually need the jump because a reset should happen. */
+       dram_write_jump(target, 2, false);
+       dram_write32(target, 4, info->dcsr, true);
+       cache_invalidate(target);
+
+       target->state = TARGET_RESET;
+
+       return ERROR_OK;
+}
+
+static int deassert_reset(struct target *target)
+{
+       jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+       if (target->reset_halt)
+               return wait_for_state(target, TARGET_HALTED);
+       else
+               return wait_for_state(target, TARGET_RUNNING);
+}
+
+static int read_memory(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, uint8_t *buffer)
+{
+       jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+       cache_set32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16));
+       switch (size) {
+               case 1:
+                       cache_set32(target, 1, lb(S1, S0, 0));
+                       cache_set32(target, 2, sw(S1, ZERO, DEBUG_RAM_START + 16));
+                       break;
+               case 2:
+                       cache_set32(target, 1, lh(S1, S0, 0));
+                       cache_set32(target, 2, sw(S1, ZERO, DEBUG_RAM_START + 16));
+                       break;
+               case 4:
+                       cache_set32(target, 1, lw(S1, S0, 0));
+                       cache_set32(target, 2, sw(S1, ZERO, DEBUG_RAM_START + 16));
+                       break;
+               default:
+                       LOG_ERROR("Unsupported size: %d", size);
+                       return ERROR_FAIL;
+       }
+       cache_set_jump(target, 3);
+       cache_write(target, CACHE_NO_READ, false);
+
+       riscv011_info_t *info = get_info(target);
+       const unsigned max_batch_size = 256;
+       scans_t *scans = scans_new(target, max_batch_size);
+
+       uint32_t result_value = 0x777;
+       uint32_t i = 0;
+       while (i < count + 3) {
+               unsigned int batch_size = MIN(count + 3 - i, max_batch_size);
+               scans_reset(scans);
+
+               for (unsigned int j = 0; j < batch_size; j++) {
+                       if (i + j == count) {
+                               /* Just insert a read so we can scan out the last value. */
+                               scans_add_read32(scans, 4, false);
+                       } else if (i + j >= count + 1) {
+                               /* And check for errors. */
+                               scans_add_read32(scans, info->dramsize-1, false);
+                       } else {
+                               /* Write the next address and set interrupt. */
+                               uint32_t offset = size * (i + j);
+                               scans_add_write32(scans, 4, address + offset, true);
+                       }
+               }
+
+               int retval = scans_execute(scans);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("JTAG execute failed: %d", retval);
+                       goto error;
+               }
+
+               int dbus_busy = 0;
+               int execute_busy = 0;
+               for (unsigned int j = 0; j < batch_size; j++) {
+                       dbus_status_t status = scans_get_u32(scans, j, DBUS_OP_START,
+                                       DBUS_OP_SIZE);
+                       switch (status) {
+                               case DBUS_STATUS_SUCCESS:
+                                       break;
+                               case DBUS_STATUS_FAILED:
+                                       LOG_ERROR("Debug RAM write failed. Hardware error?");
+                                       goto error;
+                               case DBUS_STATUS_BUSY:
+                                       dbus_busy++;
+                                       break;
+                               default:
+                                       LOG_ERROR("Got invalid bus access status: %d", status);
+                                       return ERROR_FAIL;
+                       }
+                       uint64_t data = scans_get_u64(scans, j, DBUS_DATA_START,
+                                       DBUS_DATA_SIZE);
+                       if (data & DMCONTROL_INTERRUPT)
+                               execute_busy++;
+                       if (i + j == count + 2) {
+                               result_value = data;
+                       } else if (i + j > 1) {
+                               uint32_t offset = size * (i + j - 2);
+                               switch (size) {
+                                       case 1:
+                                               buffer[offset] = data;
+                                               break;
+                                       case 2:
+                                               buffer[offset] = data;
+                                               buffer[offset+1] = data >> 8;
+                                               break;
+                                       case 4:
+                                               buffer[offset] = data;
+                                               buffer[offset+1] = data >> 8;
+                                               buffer[offset+2] = data >> 16;
+                                               buffer[offset+3] = data >> 24;
+                                               break;
+                               }
+                       }
+                       LOG_DEBUG("j=%d status=%d data=%09" PRIx64, j, status, data);
+               }
+               if (dbus_busy)
+                       increase_dbus_busy_delay(target);
+               if (execute_busy)
+                       increase_interrupt_high_delay(target);
+               if (dbus_busy || execute_busy) {
+                       wait_for_debugint_clear(target, false);
+
+                       /* Retry. */
+                       LOG_INFO("Retrying memory read starting from 0x%" TARGET_PRIxADDR
+                                       " with more delays", address + size * i);
+               } else {
+                       i += batch_size;
+               }
+       }
+
+       if (result_value != 0) {
+               LOG_USER("Core got an exception (0x%x) while reading from 0x%"
+                               TARGET_PRIxADDR, result_value, address + size * (count-1));
+               if (count > 1) {
+                       LOG_USER("(It may have failed between 0x%" TARGET_PRIxADDR
+                                       " and 0x%" TARGET_PRIxADDR " as well, but we "
+                                       "didn't check then.)",
+                                       address, address + size * (count-2) + size - 1);
+               }
+               goto error;
+       }
+
+       scans_delete(scans);
+       cache_clean(target);
+       return ERROR_OK;
+
+error:
+       scans_delete(scans);
+       cache_clean(target);
+       return ERROR_FAIL;
+}
+
+static int setup_write_memory(struct target *target, uint32_t size)
+{
+       switch (size) {
+               case 1:
+                       cache_set32(target, 0, lb(S0, ZERO, DEBUG_RAM_START + 16));
+                       cache_set32(target, 1, sb(S0, T0, 0));
+                       break;
+               case 2:
+                       cache_set32(target, 0, lh(S0, ZERO, DEBUG_RAM_START + 16));
+                       cache_set32(target, 1, sh(S0, T0, 0));
+                       break;
+               case 4:
+                       cache_set32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16));
+                       cache_set32(target, 1, sw(S0, T0, 0));
+                       break;
+               default:
+                       LOG_ERROR("Unsupported size: %d", size);
+                       return ERROR_FAIL;
+       }
+       cache_set32(target, 2, addi(T0, T0, size));
+       cache_set_jump(target, 3);
+       cache_write(target, 4, false);
+
+       return ERROR_OK;
+}
+
+static int write_memory(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+       riscv011_info_t *info = get_info(target);
+       jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+       /* Set up the address. */
+       cache_set_store(target, 0, T0, SLOT1);
+       cache_set_load(target, 1, T0, SLOT0);
+       cache_set_jump(target, 2);
+       cache_set(target, SLOT0, address);
+       if (cache_write(target, 5, true) != ERROR_OK)
+               return ERROR_FAIL;
+
+       uint64_t t0 = cache_get(target, SLOT1);
+       LOG_DEBUG("t0 is 0x%" PRIx64, t0);
+
+       if (setup_write_memory(target, size) != ERROR_OK)
+               return ERROR_FAIL;
+
+       const unsigned max_batch_size = 256;
+       scans_t *scans = scans_new(target, max_batch_size);
+
+       uint32_t result_value = 0x777;
+       uint32_t i = 0;
+       while (i < count + 2) {
+               unsigned int batch_size = MIN(count + 2 - i, max_batch_size);
+               scans_reset(scans);
+
+               for (unsigned int j = 0; j < batch_size; j++) {
+                       if (i + j >= count) {
+                               /* Check for an exception. */
+                               scans_add_read32(scans, info->dramsize-1, false);
+                       } else {
+                               /* Write the next value and set interrupt. */
+                               uint32_t value;
+                               uint32_t offset = size * (i + j);
+                               switch (size) {
+                                       case 1:
+                                               value = buffer[offset];
+                                               break;
+                                       case 2:
+                                               value = buffer[offset] |
+                                                       (buffer[offset+1] << 8);
+                                               break;
+                                       case 4:
+                                               value = buffer[offset] |
+                                                       ((uint32_t) buffer[offset+1] << 8) |
+                                                       ((uint32_t) buffer[offset+2] << 16) |
+                                                       ((uint32_t) buffer[offset+3] << 24);
+                                               break;
+                                       default:
+                                               goto error;
+                               }
+
+                               scans_add_write32(scans, 4, value, true);
+                       }
+               }
+
+               int retval = scans_execute(scans);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("JTAG execute failed: %d", retval);
+                       goto error;
+               }
+
+               int dbus_busy = 0;
+               int execute_busy = 0;
+               for (unsigned int j = 0; j < batch_size; j++) {
+                       dbus_status_t status = scans_get_u32(scans, j, DBUS_OP_START,
+                                       DBUS_OP_SIZE);
+                       switch (status) {
+                               case DBUS_STATUS_SUCCESS:
+                                       break;
+                               case DBUS_STATUS_FAILED:
+                                       LOG_ERROR("Debug RAM write failed. Hardware error?");
+                                       goto error;
+                               case DBUS_STATUS_BUSY:
+                                       dbus_busy++;
+                                       break;
+                               default:
+                                       LOG_ERROR("Got invalid bus access status: %d", status);
+                                       return ERROR_FAIL;
+                       }
+                       int interrupt = scans_get_u32(scans, j, DBUS_DATA_START + 33, 1);
+                       if (interrupt)
+                               execute_busy++;
+                       if (i + j == count + 1)
+                               result_value = scans_get_u32(scans, j, DBUS_DATA_START, 32);
+               }
+               if (dbus_busy)
+                       increase_dbus_busy_delay(target);
+               if (execute_busy)
+                       increase_interrupt_high_delay(target);
+               if (dbus_busy || execute_busy) {
+                       wait_for_debugint_clear(target, false);
+
+                       /* Retry.
+                        * Set t0 back to what it should have been at the beginning of this
+                        * batch. */
+                       LOG_INFO("Retrying memory write starting from 0x%" TARGET_PRIxADDR
+                                       " with more delays", address + size * i);
+
+                       cache_clean(target);
+
+                       if (write_gpr(target, T0, address + size * i) != ERROR_OK)
+                               goto error;
+
+                       if (setup_write_memory(target, size) != ERROR_OK)
+                               goto error;
+               } else {
+                       i += batch_size;
+               }
+       }
+
+       if (result_value != 0) {
+               LOG_ERROR("Core got an exception (0x%x) while writing to 0x%"
+                               TARGET_PRIxADDR, result_value, address + size * (count-1));
+               if (count > 1) {
+                       LOG_ERROR("(It may have failed between 0x%" TARGET_PRIxADDR
+                                       " and 0x%" TARGET_PRIxADDR " as well, but we "
+                                       "didn't check then.)",
+                                       address, address + size * (count-2) + size - 1);
+               }
+               goto error;
+       }
+
+       scans_delete(scans);
+       cache_clean(target);
+       return register_write(target, T0, t0);
+
+error:
+       scans_delete(scans);
+       cache_clean(target);
+       return ERROR_FAIL;
+}
+
+static int arch_state(struct target *target)
+{
+       return ERROR_OK;
+}
+
+struct target_type riscv011_target = {
+       .name = "riscv",
+
+       .init_target = init_target,
+       .deinit_target = deinit_target,
+       .examine = examine,
+
+       /* poll current target status */
+       .poll = riscv011_poll,
+
+       .halt = halt,
+       .resume = riscv011_resume,
+       .step = step,
+
+       .assert_reset = assert_reset,
+       .deassert_reset = deassert_reset,
+
+       .read_memory = read_memory,
+       .write_memory = write_memory,
+
+       .arch_state = arch_state,
+};
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
new file mode 100644 (file)
index 0000000..4acd427
--- /dev/null
@@ -0,0 +1,3036 @@
+/*
+ * Support for RISC-V, debug version 0.13, which is currently (2/4/17) the
+ * latest draft.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <time.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "target/target.h"
+#include "target/algorithm.h"
+#include "target/target_type.h"
+#include "log.h"
+#include "jtag/jtag.h"
+#include "target/register.h"
+#include "target/breakpoints.h"
+#include "helper/time_support.h"
+#include "helper/list.h"
+#include "riscv.h"
+#include "debug_defines.h"
+#include "rtos/rtos.h"
+#include "program.h"
+#include "asm.h"
+#include "batch.h"
+
+#define DMI_DATA1 (DMI_DATA0 + 1)
+#define DMI_PROGBUF1 (DMI_PROGBUF0 + 1)
+
+static int riscv013_on_step_or_resume(struct target *target, bool step);
+static int riscv013_step_or_resume_current_hart(struct target *target, bool step);
+static void riscv013_clear_abstract_error(struct target *target);
+
+/* Implementations of the functions in riscv_info_t. */
+static int riscv013_get_register(struct target *target,
+               riscv_reg_t *value, int hid, int rid);
+static int riscv013_set_register(struct target *target, int hartid, int regid, uint64_t value);
+static int riscv013_select_current_hart(struct target *target);
+static int riscv013_halt_current_hart(struct target *target);
+static int riscv013_resume_current_hart(struct target *target);
+static int riscv013_step_current_hart(struct target *target);
+static int riscv013_on_halt(struct target *target);
+static int riscv013_on_step(struct target *target);
+static int riscv013_on_resume(struct target *target);
+static bool riscv013_is_halted(struct target *target);
+static enum riscv_halt_reason riscv013_halt_reason(struct target *target);
+static int riscv013_write_debug_buffer(struct target *target, unsigned index,
+               riscv_insn_t d);
+static riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned
+               index);
+static int riscv013_execute_debug_buffer(struct target *target);
+static void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d);
+static void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a);
+static int riscv013_dmi_write_u64_bits(struct target *target);
+static void riscv013_fill_dmi_nop_u64(struct target *target, char *buf);
+static int register_read(struct target *target, uint64_t *value, uint32_t number);
+static int register_read_direct(struct target *target, uint64_t *value, uint32_t number);
+static int register_write_direct(struct target *target, unsigned number,
+               uint64_t value);
+static int read_memory(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, uint8_t *buffer);
+static int write_memory(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, const uint8_t *buffer);
+
+/**
+ * Since almost everything can be accomplish by scanning the dbus register, all
+ * functions here assume dbus is already selected. The exception are functions
+ * called directly by OpenOCD, which can't assume anything about what's
+ * currently in IR. They should set IR to dbus explicitly.
+ */
+
+#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
+#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
+
+#define DIM(x)         (sizeof(x)/sizeof(*x))
+
+#define CSR_DCSR_CAUSE_SWBP            1
+#define CSR_DCSR_CAUSE_TRIGGER 2
+#define CSR_DCSR_CAUSE_DEBUGINT        3
+#define CSR_DCSR_CAUSE_STEP            4
+#define CSR_DCSR_CAUSE_HALT            5
+
+#define RISCV013_INFO(r) riscv013_info_t *r = get_info(target)
+
+/*** JTAG registers. ***/
+
+typedef enum {
+       DMI_OP_NOP = 0,
+       DMI_OP_READ = 1,
+       DMI_OP_WRITE = 2
+} dmi_op_t;
+typedef enum {
+       DMI_STATUS_SUCCESS = 0,
+       DMI_STATUS_FAILED = 2,
+       DMI_STATUS_BUSY = 3
+} dmi_status_t;
+
+typedef enum {
+       RE_OK,
+       RE_FAIL,
+       RE_AGAIN
+} riscv_error_t;
+
+typedef enum slot {
+       SLOT0,
+       SLOT1,
+       SLOT_LAST,
+} slot_t;
+
+/*** Debug Bus registers. ***/
+
+#define CMDERR_NONE                            0
+#define CMDERR_BUSY                            1
+#define CMDERR_NOT_SUPPORTED   2
+#define CMDERR_EXCEPTION               3
+#define CMDERR_HALT_RESUME             4
+#define CMDERR_OTHER                   7
+
+/*** Info about the core being debugged. ***/
+
+struct trigger {
+       uint64_t address;
+       uint32_t length;
+       uint64_t mask;
+       uint64_t value;
+       bool read, write, execute;
+       int unique_id;
+};
+
+typedef enum {
+       YNM_MAYBE,
+       YNM_YES,
+       YNM_NO
+} yes_no_maybe_t;
+
+typedef struct {
+       struct list_head list;
+       int abs_chain_position;
+       /* Indicates we already reset this DM, so don't need to do it again. */
+       bool was_reset;
+       /* Targets that are connected to this DM. */
+       struct list_head target_list;
+       /* The currently selected hartid on this DM. */
+       int current_hartid;
+} dm013_info_t;
+
+typedef struct {
+       struct list_head list;
+       struct target *target;
+} target_list_t;
+
+typedef struct {
+       /* Number of address bits in the dbus register. */
+       unsigned abits;
+       /* Number of abstract command data registers. */
+       unsigned datacount;
+       /* Number of words in the Program Buffer. */
+       unsigned progbufsize;
+
+       /* We cache the read-only bits of sbcs here. */
+       uint32_t sbcs;
+
+       yes_no_maybe_t progbuf_writable;
+       /* We only need the address so that we know the alignment of the buffer. */
+       riscv_addr_t progbuf_address;
+
+       /* Number of run-test/idle cycles the target requests we do after each dbus
+        * access. */
+       unsigned int dtmcontrol_idle;
+
+       /* This value is incremented every time a dbus access comes back as "busy".
+        * It's used to determine how many run-test/idle cycles to feed the target
+        * in between accesses. */
+       unsigned int dmi_busy_delay;
+
+       /* Number of run-test/idle cycles to add between consecutive bus master
+        * reads/writes respectively. */
+       unsigned int bus_master_write_delay, bus_master_read_delay;
+
+       /* This value is increased every time we tried to execute two commands
+        * consecutively, and the second one failed because the previous hadn't
+        * completed yet.  It's used to add extra run-test/idle cycles after
+        * starting a command, so we don't have to waste time checking for busy to
+        * go low. */
+       unsigned int ac_busy_delay;
+
+       bool need_strict_step;
+
+       bool abstract_read_csr_supported;
+       bool abstract_write_csr_supported;
+       bool abstract_read_fpr_supported;
+       bool abstract_write_fpr_supported;
+
+       /* When a function returns some error due to a failure indicated by the
+        * target in cmderr, the caller can look here to see what that error was.
+        * (Compare with errno.) */
+       uint8_t cmderr;
+
+       /* Some fields from hartinfo. */
+       uint8_t datasize;
+       uint8_t dataaccess;
+       int16_t dataaddr;
+
+       /* The width of the hartsel field. */
+       unsigned hartsellen;
+
+       /* DM that provides access to this target. */
+       dm013_info_t *dm;
+} riscv013_info_t;
+
+LIST_HEAD(dm_list);
+
+static riscv013_info_t *get_info(const struct target *target)
+{
+       riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       return (riscv013_info_t *) info->version_specific;
+}
+
+/**
+ * Return the DM structure for this target. If there isn't one, find it in the
+ * global list of DMs. If it's not in there, then create one and initialize it
+ * to 0.
+ */
+static dm013_info_t *get_dm(struct target *target)
+{
+       RISCV013_INFO(info);
+       if (info->dm)
+               return info->dm;
+
+       int abs_chain_position = target->tap->abs_chain_position;
+
+       dm013_info_t *entry;
+       dm013_info_t *dm = NULL;
+       list_for_each_entry(entry, &dm_list, list) {
+               if (entry->abs_chain_position == abs_chain_position) {
+                       dm = entry;
+                       break;
+               }
+       }
+
+       if (!dm) {
+               dm = calloc(1, sizeof(dm013_info_t));
+               dm->abs_chain_position = abs_chain_position;
+               dm->current_hartid = -1;
+               INIT_LIST_HEAD(&dm->target_list);
+               list_add(&dm->list, &dm_list);
+       }
+
+       info->dm = dm;
+       target_list_t *target_entry;
+       list_for_each_entry(target_entry, &dm->target_list, list) {
+               if (target_entry->target == target)
+                       return dm;
+       }
+       target_entry = calloc(1, sizeof(*target_entry));
+       target_entry->target = target;
+       list_add(&target_entry->list, &dm->target_list);
+
+       return dm;
+}
+
+static uint32_t set_hartsel(uint32_t initial, uint32_t index)
+{
+       initial &= ~DMI_DMCONTROL_HARTSELLO;
+       initial &= ~DMI_DMCONTROL_HARTSELHI;
+
+       uint32_t index_lo = index & ((1 << DMI_DMCONTROL_HARTSELLO_LENGTH) - 1);
+       initial |= index_lo << DMI_DMCONTROL_HARTSELLO_OFFSET;
+       uint32_t index_hi = index >> DMI_DMCONTROL_HARTSELLO_LENGTH;
+       assert(index_hi < 1 << DMI_DMCONTROL_HARTSELHI_LENGTH);
+       initial |= index_hi << DMI_DMCONTROL_HARTSELHI_OFFSET;
+
+       return initial;
+}
+
+static void decode_dmi(char *text, unsigned address, unsigned data)
+{
+       static const struct {
+               unsigned address;
+               uint64_t mask;
+               const char *name;
+       } description[] = {
+               { DMI_DMCONTROL, DMI_DMCONTROL_HALTREQ, "haltreq" },
+               { DMI_DMCONTROL, DMI_DMCONTROL_RESUMEREQ, "resumereq" },
+               { DMI_DMCONTROL, DMI_DMCONTROL_HARTRESET, "hartreset" },
+               { DMI_DMCONTROL, DMI_DMCONTROL_HASEL, "hasel" },
+               { DMI_DMCONTROL, DMI_DMCONTROL_HARTSELHI, "hartselhi" },
+               { DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO, "hartsello" },
+               { DMI_DMCONTROL, DMI_DMCONTROL_NDMRESET, "ndmreset" },
+               { DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE, "dmactive" },
+               { DMI_DMCONTROL, DMI_DMCONTROL_ACKHAVERESET, "ackhavereset" },
+
+               { DMI_DMSTATUS, DMI_DMSTATUS_IMPEBREAK, "impebreak" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_ALLHAVERESET, "allhavereset" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_ANYHAVERESET, "anyhavereset" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_ALLRESUMEACK, "allresumeack" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_ANYRESUMEACK, "anyresumeack" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_ALLNONEXISTENT, "allnonexistent" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_ANYNONEXISTENT, "anynonexistent" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_ALLUNAVAIL, "allunavail" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_ANYUNAVAIL, "anyunavail" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_ALLRUNNING, "allrunning" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_ANYRUNNING, "anyrunning" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_ALLHALTED, "allhalted" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_ANYHALTED, "anyhalted" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_AUTHENTICATED, "authenticated" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_AUTHBUSY, "authbusy" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_DEVTREEVALID, "devtreevalid" },
+               { DMI_DMSTATUS, DMI_DMSTATUS_VERSION, "version" },
+
+               { DMI_ABSTRACTCS, DMI_ABSTRACTCS_PROGBUFSIZE, "progbufsize" },
+               { DMI_ABSTRACTCS, DMI_ABSTRACTCS_BUSY, "busy" },
+               { DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR, "cmderr" },
+               { DMI_ABSTRACTCS, DMI_ABSTRACTCS_DATACOUNT, "datacount" },
+
+               { DMI_COMMAND, DMI_COMMAND_CMDTYPE, "cmdtype" },
+
+               { DMI_SBCS, DMI_SBCS_SBREADONADDR, "sbreadonaddr" },
+               { DMI_SBCS, DMI_SBCS_SBACCESS, "sbaccess" },
+               { DMI_SBCS, DMI_SBCS_SBAUTOINCREMENT, "sbautoincrement" },
+               { DMI_SBCS, DMI_SBCS_SBREADONDATA, "sbreadondata" },
+               { DMI_SBCS, DMI_SBCS_SBERROR, "sberror" },
+               { DMI_SBCS, DMI_SBCS_SBASIZE, "sbasize" },
+               { DMI_SBCS, DMI_SBCS_SBACCESS128, "sbaccess128" },
+               { DMI_SBCS, DMI_SBCS_SBACCESS64, "sbaccess64" },
+               { DMI_SBCS, DMI_SBCS_SBACCESS32, "sbaccess32" },
+               { DMI_SBCS, DMI_SBCS_SBACCESS16, "sbaccess16" },
+               { DMI_SBCS, DMI_SBCS_SBACCESS8, "sbaccess8" },
+       };
+
+       text[0] = 0;
+       for (unsigned i = 0; i < DIM(description); i++) {
+               if (description[i].address == address) {
+                       uint64_t mask = description[i].mask;
+                       unsigned value = get_field(data, mask);
+                       if (value) {
+                               if (i > 0)
+                                       *(text++) = ' ';
+                               if (mask & (mask >> 1)) {
+                                       /* If the field is more than 1 bit wide. */
+                                       sprintf(text, "%s=%d", description[i].name, value);
+                               } else {
+                                       strcpy(text, description[i].name);
+                               }
+                               text += strlen(text);
+                       }
+               }
+       }
+}
+
+static 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;
+
+       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;
+
+       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__, "scan",
+                       "%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);
+
+       char out_text[500];
+       char in_text[500];
+       decode_dmi(out_text, out_address, out_data);
+       decode_dmi(in_text, in_address, in_data);
+       if (in_text[0] || out_text[0]) {
+               log_printf_lf(LOG_LVL_DEBUG, __FILE__, __LINE__, "scan", "%s -> %s",
+                               out_text, in_text);
+       }
+}
+
+/*** Utility functions. ***/
+
+static void select_dmi(struct target *target)
+{
+       static uint8_t ir_dmi[1] = {DTM_DMI};
+       struct scan_field field = {
+               .num_bits = target->tap->ir_length,
+               .out_value = ir_dmi,
+               .in_value = NULL,
+               .check_value = NULL,
+               .check_mask = NULL
+       };
+
+       jtag_add_ir_scan(target->tap, &field, TAP_IDLE);
+}
+
+static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
+{
+       struct scan_field field;
+       uint8_t in_value[4];
+       uint8_t out_value[4];
+
+       buf_set_u32(out_value, 0, 32, out);
+
+       jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE);
+
+       field.num_bits = 32;
+       field.out_value = out_value;
+       field.in_value = in_value;
+       jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+       /* Always return to dmi. */
+       select_dmi(target);
+
+       int retval = jtag_execute_queue();
+       if (retval != ERROR_OK) {
+               LOG_ERROR("failed jtag scan: %d", retval);
+               return retval;
+       }
+
+       uint32_t in = buf_get_u32(field.in_value, 0, 32);
+       LOG_DEBUG("DTMCS: 0x%x -> 0x%x", out, in);
+
+       return in;
+}
+
+static void increase_dmi_busy_delay(struct target *target)
+{
+       riscv013_info_t *info = get_info(target);
+       info->dmi_busy_delay += info->dmi_busy_delay / 10 + 1;
+       LOG_DEBUG("dtmcontrol_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
+                       info->dtmcontrol_idle, info->dmi_busy_delay,
+                       info->ac_busy_delay);
+
+       dtmcontrol_scan(target, DTM_DTMCS_DMIRESET);
+}
+
+/**
+ * exec: If this is set, assume the scan results in an execution, so more
+ * run-test/idle cycles may be required.
+ */
+static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
+               uint32_t *data_in, dmi_op_t op, uint32_t address_out, uint32_t data_out,
+               bool exec)
+{
+       riscv013_info_t *info = get_info(target);
+       uint8_t in[8] = {0};
+       uint8_t out[8];
+       struct scan_field field = {
+               .num_bits = info->abits + DTM_DMI_OP_LENGTH + DTM_DMI_DATA_LENGTH,
+               .out_value = out,
+               .in_value = in
+       };
+
+       assert(info->abits != 0);
+
+       buf_set_u32(out, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, op);
+       buf_set_u32(out, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, data_out);
+       buf_set_u32(out, DTM_DMI_ADDRESS_OFFSET, info->abits, address_out);
+
+       /* Assume dbus is already selected. */
+       jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+       int idle_count = info->dmi_busy_delay;
+       if (exec)
+               idle_count += info->ac_busy_delay;
+
+       if (idle_count)
+               jtag_add_runtest(idle_count, TAP_IDLE);
+
+       int retval = jtag_execute_queue();
+       if (retval != ERROR_OK) {
+               LOG_ERROR("dmi_scan failed jtag scan");
+               return DMI_STATUS_FAILED;
+       }
+
+       if (data_in)
+               *data_in = buf_get_u32(in, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH);
+
+       if (address_in)
+               *address_in = buf_get_u32(in, DTM_DMI_ADDRESS_OFFSET, info->abits);
+
+       dump_field(&field);
+
+       return buf_get_u32(in, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH);
+}
+
+static int dmi_op_timeout(struct target *target, uint32_t *data_in, int dmi_op,
+               uint32_t address, uint32_t data_out, int timeout_sec)
+{
+       select_dmi(target);
+
+       dmi_status_t status;
+       uint32_t address_in;
+
+       const char *op_name;
+       switch (dmi_op) {
+               case DMI_OP_NOP:
+                       op_name = "nop";
+                       break;
+               case DMI_OP_READ:
+                       op_name = "read";
+                       break;
+               case DMI_OP_WRITE:
+                       op_name = "write";
+                       break;
+               default:
+                       LOG_ERROR("Invalid DMI operation: %d", dmi_op);
+                       return ERROR_FAIL;
+       }
+
+       time_t start = time(NULL);
+       /* This first loop performs the request.  Note that if for some reason this
+        * stays busy, it is actually due to the previous access. */
+       while (1) {
+               status = dmi_scan(target, NULL, NULL, dmi_op, address, data_out,
+                               false);
+               if (status == DMI_STATUS_BUSY) {
+                       increase_dmi_busy_delay(target);
+               } else if (status == DMI_STATUS_SUCCESS) {
+                       break;
+               } else {
+                       LOG_ERROR("failed %s at 0x%x, status=%d", op_name, address, status);
+                       return ERROR_FAIL;
+               }
+               if (time(NULL) - start > timeout_sec)
+                       return ERROR_TIMEOUT_REACHED;
+       }
+
+       if (status != DMI_STATUS_SUCCESS) {
+               LOG_ERROR("Failed %s at 0x%x; status=%d", op_name, address, status);
+               return ERROR_FAIL;
+       }
+
+       /* This second loop ensures the request succeeded, and gets back data.
+        * Note that NOP can result in a 'busy' result as well, but that would be
+        * noticed on the next DMI access we do. */
+       while (1) {
+               status = dmi_scan(target, &address_in, data_in, DMI_OP_NOP, address, 0,
+                               false);
+               if (status == DMI_STATUS_BUSY) {
+                       increase_dmi_busy_delay(target);
+               } else if (status == DMI_STATUS_SUCCESS) {
+                       break;
+               } else {
+                       LOG_ERROR("failed %s (NOP) at 0x%x, status=%d", op_name, address,
+                                       status);
+                       return ERROR_FAIL;
+               }
+               if (time(NULL) - start > timeout_sec)
+                       return ERROR_TIMEOUT_REACHED;
+       }
+
+       if (status != DMI_STATUS_SUCCESS) {
+               if (status == DMI_STATUS_FAILED || !data_in) {
+                       LOG_ERROR("Failed %s (NOP) at 0x%x; status=%d", op_name, address,
+                                       status);
+               } else {
+                       LOG_ERROR("Failed %s (NOP) at 0x%x; value=0x%x, status=%d",
+                                       op_name, address, *data_in, status);
+               }
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+static int dmi_op(struct target *target, uint32_t *data_in, int dmi_op,
+               uint32_t address, uint32_t data_out)
+{
+       int result = dmi_op_timeout(target, data_in, dmi_op, address, data_out,
+                       riscv_command_timeout_sec);
+       if (result == ERROR_TIMEOUT_REACHED) {
+               LOG_ERROR("DMI operation didn't complete in %d seconds. The target is "
+                               "either really slow or broken. You could increase the "
+                               "timeout with riscv set_command_timeout_sec.",
+                               riscv_command_timeout_sec);
+               return ERROR_FAIL;
+       }
+       return result;
+}
+
+static int dmi_read(struct target *target, uint32_t *value, uint32_t address)
+{
+       return dmi_op(target, value, DMI_OP_READ, address, 0);
+}
+
+static int dmi_write(struct target *target, uint32_t address, uint32_t value)
+{
+       return dmi_op(target, NULL, DMI_OP_WRITE, address, value);
+}
+
+int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus,
+               bool authenticated, unsigned timeout_sec)
+{
+       int result = dmi_op_timeout(target, dmstatus, DMI_OP_READ, DMI_DMSTATUS, 0,
+                       timeout_sec);
+       if (result != ERROR_OK)
+               return result;
+       if (authenticated && !get_field(*dmstatus, DMI_DMSTATUS_AUTHENTICATED)) {
+               LOG_ERROR("Debugger is not authenticated to target Debug Module. "
+                               "(dmstatus=0x%x). Use `riscv authdata_read` and "
+                               "`riscv authdata_write` commands to authenticate.", *dmstatus);
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
+int dmstatus_read(struct target *target, uint32_t *dmstatus,
+               bool authenticated)
+{
+       return dmstatus_read_timeout(target, dmstatus, authenticated,
+                       riscv_command_timeout_sec);
+}
+
+static void increase_ac_busy_delay(struct target *target)
+{
+       riscv013_info_t *info = get_info(target);
+       info->ac_busy_delay += info->ac_busy_delay / 10 + 1;
+       LOG_DEBUG("dtmcontrol_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
+                       info->dtmcontrol_idle, info->dmi_busy_delay,
+                       info->ac_busy_delay);
+}
+
+uint32_t abstract_register_size(unsigned width)
+{
+       switch (width) {
+               case 32:
+                       return set_field(0, AC_ACCESS_REGISTER_SIZE, 2);
+               case 64:
+                       return set_field(0, AC_ACCESS_REGISTER_SIZE, 3);
+                       break;
+               case 128:
+                       return set_field(0, AC_ACCESS_REGISTER_SIZE, 4);
+                       break;
+               default:
+                       LOG_ERROR("Unsupported register width: %d", width);
+                       return 0;
+       }
+}
+
+static int wait_for_idle(struct target *target, uint32_t *abstractcs)
+{
+       RISCV013_INFO(info);
+       time_t start = time(NULL);
+       while (1) {
+               if (dmi_read(target, abstractcs, DMI_ABSTRACTCS) != ERROR_OK)
+                       return ERROR_FAIL;
+
+               if (get_field(*abstractcs, DMI_ABSTRACTCS_BUSY) == 0)
+                       return ERROR_OK;
+
+               if (time(NULL) - start > riscv_command_timeout_sec) {
+                       info->cmderr = get_field(*abstractcs, DMI_ABSTRACTCS_CMDERR);
+                       if (info->cmderr != CMDERR_NONE) {
+                               const char *errors[8] = {
+                                       "none",
+                                       "busy",
+                                       "not supported",
+                                       "exception",
+                                       "halt/resume",
+                                       "reserved",
+                                       "reserved",
+                                       "other" };
+
+                               LOG_ERROR("Abstract command ended in error '%s' (abstractcs=0x%x)",
+                                               errors[info->cmderr], *abstractcs);
+                       }
+
+                       LOG_ERROR("Timed out after %ds waiting for busy to go low (abstractcs=0x%x). "
+                                       "Increase the timeout with riscv set_command_timeout_sec.",
+                                       riscv_command_timeout_sec,
+                                       *abstractcs);
+                       return ERROR_FAIL;
+               }
+       }
+}
+
+static int execute_abstract_command(struct target *target, uint32_t command)
+{
+       RISCV013_INFO(info);
+       LOG_DEBUG("command=0x%x", command);
+       dmi_write(target, DMI_COMMAND, command);
+
+       uint32_t abstractcs = 0;
+       wait_for_idle(target, &abstractcs);
+
+       info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR);
+       if (info->cmderr != 0) {
+               LOG_DEBUG("command 0x%x failed; abstractcs=0x%x", command, abstractcs);
+               /* Clear the error. */
+               dmi_write(target, DMI_ABSTRACTCS, set_field(0, DMI_ABSTRACTCS_CMDERR,
+                                       info->cmderr));
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+static riscv_reg_t read_abstract_arg(struct target *target, unsigned index,
+               unsigned size_bits)
+{
+       riscv_reg_t value = 0;
+       uint32_t v;
+       unsigned offset = index * size_bits / 32;
+       switch (size_bits) {
+               default:
+                       LOG_ERROR("Unsupported size: %d", size_bits);
+                       return ~0;
+               case 64:
+                       dmi_read(target, &v, DMI_DATA0 + offset + 1);
+                       value |= ((uint64_t) v) << 32;
+                       /* falls through */
+               case 32:
+                       dmi_read(target, &v, DMI_DATA0 + offset);
+                       value |= v;
+       }
+       return value;
+}
+
+static int write_abstract_arg(struct target *target, unsigned index,
+               riscv_reg_t value, unsigned size_bits)
+{
+       unsigned offset = index * size_bits / 32;
+       switch (size_bits) {
+               default:
+                       LOG_ERROR("Unsupported size: %d", size_bits);
+                       return ERROR_FAIL;
+               case 64:
+                       dmi_write(target, DMI_DATA0 + offset + 1, value >> 32);
+                       /* falls through */
+               case 32:
+                       dmi_write(target, DMI_DATA0 + offset, value);
+       }
+       return ERROR_OK;
+}
+
+/**
+ * @size in bits
+ */
+static uint32_t access_register_command(uint32_t number, unsigned size,
+               uint32_t flags)
+{
+       uint32_t command = set_field(0, DMI_COMMAND_CMDTYPE, 0);
+       switch (size) {
+               case 32:
+                       command = set_field(command, AC_ACCESS_REGISTER_SIZE, 2);
+                       break;
+               case 64:
+                       command = set_field(command, AC_ACCESS_REGISTER_SIZE, 3);
+                       break;
+               default:
+                       assert(0);
+       }
+
+       if (number <= GDB_REGNO_XPR31) {
+               command = set_field(command, AC_ACCESS_REGISTER_REGNO,
+                               0x1000 + number - GDB_REGNO_ZERO);
+       } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+               command = set_field(command, AC_ACCESS_REGISTER_REGNO,
+                               0x1020 + number - GDB_REGNO_FPR0);
+       } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+               command = set_field(command, AC_ACCESS_REGISTER_REGNO,
+                               number - GDB_REGNO_CSR0);
+       } else {
+               assert(0);
+       }
+
+       command |= flags;
+
+       return command;
+}
+
+static int register_read_abstract(struct target *target, uint64_t *value,
+               uint32_t number, unsigned size)
+{
+       RISCV013_INFO(info);
+
+       if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
+                       !info->abstract_read_fpr_supported)
+               return ERROR_FAIL;
+       if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 &&
+                       !info->abstract_read_csr_supported)
+               return ERROR_FAIL;
+
+       uint32_t command = access_register_command(number, size,
+                       AC_ACCESS_REGISTER_TRANSFER);
+
+       int result = execute_abstract_command(target, command);
+       if (result != ERROR_OK) {
+               if (info->cmderr == CMDERR_NOT_SUPPORTED) {
+                       if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+                               info->abstract_read_fpr_supported = false;
+                               LOG_INFO("Disabling abstract command reads from FPRs.");
+                       } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+                               info->abstract_read_csr_supported = false;
+                               LOG_INFO("Disabling abstract command reads from CSRs.");
+                       }
+               }
+               return result;
+       }
+
+       if (value)
+               *value = read_abstract_arg(target, 0, size);
+
+       return ERROR_OK;
+}
+
+static int register_write_abstract(struct target *target, uint32_t number,
+               uint64_t value, unsigned size)
+{
+       RISCV013_INFO(info);
+
+       if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
+                       !info->abstract_write_fpr_supported)
+               return ERROR_FAIL;
+       if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 &&
+                       !info->abstract_write_csr_supported)
+               return ERROR_FAIL;
+
+       uint32_t command = access_register_command(number, size,
+                       AC_ACCESS_REGISTER_TRANSFER |
+                       AC_ACCESS_REGISTER_WRITE);
+
+       if (write_abstract_arg(target, 0, value, size) != ERROR_OK)
+               return ERROR_FAIL;
+
+       int result = execute_abstract_command(target, command);
+       if (result != ERROR_OK) {
+               if (info->cmderr == CMDERR_NOT_SUPPORTED) {
+                       if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+                               info->abstract_write_fpr_supported = false;
+                               LOG_INFO("Disabling abstract command writes to FPRs.");
+                       } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+                               info->abstract_write_csr_supported = false;
+                               LOG_INFO("Disabling abstract command writes to CSRs.");
+                       }
+               }
+               return result;
+       }
+
+       return ERROR_OK;
+}
+
+static int examine_progbuf(struct target *target)
+{
+       riscv013_info_t *info = get_info(target);
+
+       if (info->progbuf_writable != YNM_MAYBE)
+               return ERROR_OK;
+
+       /* Figure out if progbuf is writable. */
+
+       if (info->progbufsize < 1) {
+               info->progbuf_writable = YNM_NO;
+               LOG_INFO("No program buffer present.");
+               return ERROR_OK;
+       }
+
+       uint64_t s0;
+       if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+               return ERROR_FAIL;
+
+       struct riscv_program program;
+       riscv_program_init(&program, target);
+       riscv_program_insert(&program, auipc(S0));
+       if (riscv_program_exec(&program, target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       if (register_read_direct(target, &info->progbuf_address, GDB_REGNO_S0) != ERROR_OK)
+               return ERROR_FAIL;
+
+       riscv_program_init(&program, target);
+       riscv_program_insert(&program, sw(S0, S0, 0));
+       int result = riscv_program_exec(&program, target);
+
+       if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+               return ERROR_FAIL;
+
+       if (result != ERROR_OK) {
+               /* This program might have failed if the program buffer is not
+                * writable. */
+               info->progbuf_writable = YNM_NO;
+               return ERROR_OK;
+       }
+
+       uint32_t written;
+       if (dmi_read(target, &written, DMI_PROGBUF0) != ERROR_OK)
+               return ERROR_FAIL;
+       if (written == (uint32_t) info->progbuf_address) {
+               LOG_INFO("progbuf is writable at 0x%" PRIx64,
+                               info->progbuf_address);
+               info->progbuf_writable = YNM_YES;
+
+       } else {
+               LOG_INFO("progbuf is not writeable at 0x%" PRIx64,
+                               info->progbuf_address);
+               info->progbuf_writable = YNM_NO;
+       }
+
+       return ERROR_OK;
+}
+
+typedef enum {
+       SPACE_DMI_DATA,
+       SPACE_DMI_PROGBUF,
+       SPACE_DMI_RAM
+} memory_space_t;
+
+typedef struct {
+       /* How can the debugger access this memory? */
+       memory_space_t memory_space;
+       /* Memory address to access the scratch memory from the hart. */
+       riscv_addr_t hart_address;
+       /* Memory address to access the scratch memory from the debugger. */
+       riscv_addr_t debug_address;
+       struct working_area *area;
+} scratch_mem_t;
+
+/**
+ * Find some scratch memory to be used with the given program.
+ */
+static int scratch_reserve(struct target *target,
+               scratch_mem_t *scratch,
+               struct riscv_program *program,
+               unsigned size_bytes)
+{
+       riscv_addr_t alignment = 1;
+       while (alignment < size_bytes)
+               alignment *= 2;
+
+       scratch->area = NULL;
+
+       riscv013_info_t *info = get_info(target);
+
+       if (info->dataaccess == 1) {
+               /* Sign extend dataaddr. */
+               scratch->hart_address = info->dataaddr;
+               if (info->dataaddr & (1<<11))
+                       scratch->hart_address |= 0xfffffffffffff000ULL;
+               /* Align. */
+               scratch->hart_address = (scratch->hart_address + alignment - 1) & ~(alignment - 1);
+
+               if ((size_bytes + scratch->hart_address - info->dataaddr + 3) / 4 >=
+                               info->datasize) {
+                       scratch->memory_space = SPACE_DMI_DATA;
+                       scratch->debug_address = (scratch->hart_address - info->dataaddr) / 4;
+                       return ERROR_OK;
+               }
+       }
+
+       if (examine_progbuf(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* Allow for ebreak at the end of the program. */
+       unsigned program_size = (program->instruction_count + 1) * 4;
+       scratch->hart_address = (info->progbuf_address + program_size + alignment - 1) &
+               ~(alignment - 1);
+       if ((size_bytes + scratch->hart_address - info->progbuf_address + 3) / 4 >=
+                       info->progbufsize) {
+               scratch->memory_space = SPACE_DMI_PROGBUF;
+               scratch->debug_address = (scratch->hart_address - info->progbuf_address) / 4;
+               return ERROR_OK;
+       }
+
+       if (target_alloc_working_area(target, size_bytes + alignment - 1,
+                               &scratch->area) == ERROR_OK) {
+               scratch->hart_address = (scratch->area->address + alignment - 1) &
+                       ~(alignment - 1);
+               scratch->memory_space = SPACE_DMI_RAM;
+               scratch->debug_address = scratch->hart_address;
+               return ERROR_OK;
+       }
+
+       LOG_ERROR("Couldn't find %d bytes of scratch RAM to use. Please configure "
+                       "a work area with 'configure -work-area-phys'.", size_bytes);
+       return ERROR_FAIL;
+}
+
+static int scratch_release(struct target *target,
+               scratch_mem_t *scratch)
+{
+       if (scratch->area)
+               return target_free_working_area(target, scratch->area);
+
+       return ERROR_OK;
+}
+
+static int scratch_read64(struct target *target, scratch_mem_t *scratch,
+               uint64_t *value)
+{
+       uint32_t v;
+       switch (scratch->memory_space) {
+               case SPACE_DMI_DATA:
+                       if (dmi_read(target, &v, DMI_DATA0 + scratch->debug_address) != ERROR_OK)
+                               return ERROR_FAIL;
+                       *value = v;
+                       if (dmi_read(target, &v, DMI_DATA1 + scratch->debug_address) != ERROR_OK)
+                               return ERROR_FAIL;
+                       *value |= ((uint64_t) v) << 32;
+                       break;
+               case SPACE_DMI_PROGBUF:
+                       if (dmi_read(target, &v, DMI_PROGBUF0 + scratch->debug_address) != ERROR_OK)
+                               return ERROR_FAIL;
+                       *value = v;
+                       if (dmi_read(target, &v, DMI_PROGBUF1 + scratch->debug_address) != ERROR_OK)
+                               return ERROR_FAIL;
+                       *value |= ((uint64_t) v) << 32;
+                       break;
+               case SPACE_DMI_RAM:
+                       {
+                               uint8_t buffer[8];
+                               if (read_memory(target, scratch->debug_address, 4, 2, buffer) != ERROR_OK)
+                                       return ERROR_FAIL;
+                               *value = buffer[0] |
+                                       (((uint64_t) buffer[1]) << 8) |
+                                       (((uint64_t) buffer[2]) << 16) |
+                                       (((uint64_t) buffer[3]) << 24) |
+                                       (((uint64_t) buffer[4]) << 32) |
+                                       (((uint64_t) buffer[5]) << 40) |
+                                       (((uint64_t) buffer[6]) << 48) |
+                                       (((uint64_t) buffer[7]) << 56);
+                       }
+                       break;
+       }
+       return ERROR_OK;
+}
+
+static int scratch_write64(struct target *target, scratch_mem_t *scratch,
+               uint64_t value)
+{
+       switch (scratch->memory_space) {
+               case SPACE_DMI_DATA:
+                       dmi_write(target, DMI_DATA0 + scratch->debug_address, value);
+                       dmi_write(target, DMI_DATA1 + scratch->debug_address, value >> 32);
+                       break;
+               case SPACE_DMI_PROGBUF:
+                       dmi_write(target, DMI_PROGBUF0 + scratch->debug_address, value);
+                       dmi_write(target, DMI_PROGBUF1 + scratch->debug_address, value >> 32);
+                       break;
+               case SPACE_DMI_RAM:
+                       {
+                               uint8_t buffer[8] = {
+                                       value,
+                                       value >> 8,
+                                       value >> 16,
+                                       value >> 24,
+                                       value >> 32,
+                                       value >> 40,
+                                       value >> 48,
+                                       value >> 56
+                               };
+                               if (write_memory(target, scratch->debug_address, 4, 2, buffer) != ERROR_OK)
+                                       return ERROR_FAIL;
+                       }
+                       break;
+       }
+       return ERROR_OK;
+}
+
+/** Return register size in bits. */
+static unsigned register_size(struct target *target, unsigned number)
+{
+       /* If reg_cache hasn't been initialized yet, make a guess. We need this for
+        * when this function is called during examine(). */
+       if (target->reg_cache)
+               return target->reg_cache->reg_list[number].size;
+       else
+               return riscv_xlen(target);
+}
+
+/**
+ * Immediately write the new value to the requested register. This mechanism
+ * bypasses any caches.
+ */
+static int register_write_direct(struct target *target, unsigned number,
+               uint64_t value)
+{
+       RISCV013_INFO(info);
+       RISCV_INFO(r);
+
+       LOG_DEBUG("[%d] reg[0x%x] <- 0x%" PRIx64, riscv_current_hartid(target),
+                       number, value);
+
+       int result = register_write_abstract(target, number, value,
+                       register_size(target, number));
+       if (result == ERROR_OK && target->reg_cache) {
+               struct reg *reg = &target->reg_cache->reg_list[number];
+               buf_set_u64(reg->value, 0, reg->size, value);
+               reg->valid = true;
+       }
+       if (result == ERROR_OK || info->progbufsize + r->impebreak < 2 ||
+                       !riscv_is_halted(target))
+               return result;
+
+       struct riscv_program program;
+       riscv_program_init(&program, target);
+
+       uint64_t s0;
+       if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+               return ERROR_FAIL;
+
+       scratch_mem_t scratch;
+       bool use_scratch = false;
+       if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
+                       riscv_supports_extension(target, riscv_current_hartid(target), 'D') &&
+                       riscv_xlen(target) < 64) {
+               /* There are no instructions to move all the bits from a register, so
+                * we need to use some scratch RAM. */
+               use_scratch = true;
+               riscv_program_insert(&program, fld(number - GDB_REGNO_FPR0, S0, 0));
+
+               if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
+                       return ERROR_FAIL;
+
+               if (register_write_direct(target, GDB_REGNO_S0, scratch.hart_address)
+                               != ERROR_OK) {
+                       scratch_release(target, &scratch);
+                       return ERROR_FAIL;
+               }
+
+               if (scratch_write64(target, &scratch, value) != ERROR_OK) {
+                       scratch_release(target, &scratch);
+                       return ERROR_FAIL;
+               }
+
+       } else {
+               if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK)
+                       return ERROR_FAIL;
+
+               if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+                       if (riscv_supports_extension(target, riscv_current_hartid(target), 'D'))
+                               riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0));
+                       else
+                               riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0));
+               } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+                       riscv_program_csrw(&program, S0, number);
+               } else {
+                       LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
+                       return ERROR_FAIL;
+               }
+       }
+
+       int exec_out = riscv_program_exec(&program, target);
+       /* Don't message on error. Probably the register doesn't exist. */
+       if (exec_out == ERROR_OK && target->reg_cache) {
+               struct reg *reg = &target->reg_cache->reg_list[number];
+               buf_set_u64(reg->value, 0, reg->size, value);
+               reg->valid = true;
+       }
+
+       if (use_scratch)
+               scratch_release(target, &scratch);
+
+       /* Restore S0. */
+       if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+               return ERROR_FAIL;
+
+       return exec_out;
+}
+
+/** Return the cached value, or read from the target if necessary. */
+static int register_read(struct target *target, uint64_t *value, uint32_t number)
+{
+       if (number == GDB_REGNO_ZERO) {
+               *value = 0;
+               return ERROR_OK;
+       }
+       if (target->reg_cache &&
+                       (number <= GDB_REGNO_XPR31 ||
+                        (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31))) {
+               /* Only check the cache for registers that we know won't spontaneously
+                * change. */
+               struct reg *reg = &target->reg_cache->reg_list[number];
+               if (reg && reg->valid) {
+                       *value = buf_get_u64(reg->value, 0, reg->size);
+                       return ERROR_OK;
+               }
+       }
+       int result = register_read_direct(target, value, number);
+       if (result != ERROR_OK)
+               return ERROR_FAIL;
+       if (target->reg_cache) {
+               struct reg *reg = &target->reg_cache->reg_list[number];
+               buf_set_u64(reg->value, 0, reg->size, *value);
+               reg->valid = true;
+       }
+       return ERROR_OK;
+}
+
+/** Actually read registers from the target right now. */
+static int register_read_direct(struct target *target, uint64_t *value, uint32_t number)
+{
+       RISCV013_INFO(info);
+       RISCV_INFO(r);
+
+       int result = register_read_abstract(target, value, number,
+                       register_size(target, number));
+
+       if (result != ERROR_OK &&
+                       info->progbufsize + r->impebreak >= 2 &&
+                       number > GDB_REGNO_XPR31) {
+               struct riscv_program program;
+               riscv_program_init(&program, target);
+
+               scratch_mem_t scratch;
+               bool use_scratch = false;
+
+               uint64_t s0;
+               if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+                       return ERROR_FAIL;
+
+               /* Write program to move data into s0. */
+
+               uint64_t mstatus;
+               if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+                       if (register_read(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
+                               return ERROR_FAIL;
+                       if ((mstatus & MSTATUS_FS) == 0)
+                               if (register_write_direct(target, GDB_REGNO_MSTATUS,
+                                                       set_field(mstatus, MSTATUS_FS, 1)) != ERROR_OK)
+                                       return ERROR_FAIL;
+
+                       if (riscv_supports_extension(target, riscv_current_hartid(target), 'D')
+                                       && riscv_xlen(target) < 64) {
+                               /* There are no instructions to move all the bits from a
+                                * register, so we need to use some scratch RAM. */
+                               riscv_program_insert(&program, fsd(number - GDB_REGNO_FPR0, S0,
+                                                       0));
+
+                               if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
+                                       return ERROR_FAIL;
+                               use_scratch = true;
+
+                               if (register_write_direct(target, GDB_REGNO_S0,
+                                                       scratch.hart_address) != ERROR_OK) {
+                                       scratch_release(target, &scratch);
+                                       return ERROR_FAIL;
+                               }
+                       } else if (riscv_supports_extension(target,
+                                               riscv_current_hartid(target), 'D')) {
+                               riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0));
+                       } else {
+                               riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0));
+                       }
+               } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+                       riscv_program_csrr(&program, S0, number);
+               } else {
+                       LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
+                       return ERROR_FAIL;
+               }
+
+               /* Execute program. */
+               result = riscv_program_exec(&program, target);
+               /* Don't message on error. Probably the register doesn't exist. */
+
+               if (use_scratch) {
+                       result = scratch_read64(target, &scratch, value);
+                       scratch_release(target, &scratch);
+                       if (result != ERROR_OK)
+                               return result;
+               } else {
+                       /* Read S0 */
+                       if (register_read_direct(target, value, GDB_REGNO_S0) != ERROR_OK)
+                               return ERROR_FAIL;
+               }
+
+               if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
+                               (mstatus & MSTATUS_FS) == 0)
+                       if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus) != ERROR_OK)
+                               return ERROR_FAIL;
+
+               /* Restore S0. */
+               if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+                       return ERROR_FAIL;
+       }
+
+       if (result == ERROR_OK) {
+               LOG_DEBUG("[%d] reg[0x%x] = 0x%" PRIx64, riscv_current_hartid(target),
+                               number, *value);
+       }
+
+       return result;
+}
+
+int wait_for_authbusy(struct target *target, uint32_t *dmstatus)
+{
+       time_t start = time(NULL);
+       while (1) {
+               uint32_t value;
+               if (dmstatus_read(target, &value, false) != ERROR_OK)
+                       return ERROR_FAIL;
+               if (dmstatus)
+                       *dmstatus = value;
+               if (!get_field(value, DMI_DMSTATUS_AUTHBUSY))
+                       break;
+               if (time(NULL) - start > riscv_command_timeout_sec) {
+                       LOG_ERROR("Timed out after %ds waiting for authbusy to go low (dmstatus=0x%x). "
+                                       "Increase the timeout with riscv set_command_timeout_sec.",
+                                       riscv_command_timeout_sec,
+                                       value);
+                       return ERROR_FAIL;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+/*** OpenOCD target functions. ***/
+
+static void deinit_target(struct target *target)
+{
+       LOG_DEBUG("riscv_deinit_target()");
+       riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       free(info->version_specific);
+       info->version_specific = NULL;
+}
+
+static int examine(struct target *target)
+{
+       /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
+
+       uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
+       LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
+       LOG_DEBUG("  dmireset=%d", get_field(dtmcontrol, DTM_DTMCS_DMIRESET));
+       LOG_DEBUG("  idle=%d", get_field(dtmcontrol, DTM_DTMCS_IDLE));
+       LOG_DEBUG("  dmistat=%d", get_field(dtmcontrol, DTM_DTMCS_DMISTAT));
+       LOG_DEBUG("  abits=%d", get_field(dtmcontrol, DTM_DTMCS_ABITS));
+       LOG_DEBUG("  version=%d", get_field(dtmcontrol, DTM_DTMCS_VERSION));
+       if (dtmcontrol == 0) {
+               LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power.");
+               return ERROR_FAIL;
+       }
+       if (get_field(dtmcontrol, DTM_DTMCS_VERSION) != 1) {
+               LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)",
+                               get_field(dtmcontrol, DTM_DTMCS_VERSION), dtmcontrol);
+               return ERROR_FAIL;
+       }
+
+       riscv013_info_t *info = get_info(target);
+       info->abits = get_field(dtmcontrol, DTM_DTMCS_ABITS);
+       info->dtmcontrol_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE);
+
+       uint32_t dmstatus;
+       if (dmstatus_read(target, &dmstatus, false) != ERROR_OK)
+               return ERROR_FAIL;
+       LOG_DEBUG("dmstatus:  0x%08x", dmstatus);
+       if (get_field(dmstatus, DMI_DMSTATUS_VERSION) != 2) {
+               LOG_ERROR("OpenOCD only supports Debug Module version 2, not %d "
+                               "(dmstatus=0x%x)", get_field(dmstatus, DMI_DMSTATUS_VERSION), dmstatus);
+               return ERROR_FAIL;
+       }
+
+       /* Reset the Debug Module. */
+       dm013_info_t *dm = get_dm(target);
+       if (!dm->was_reset) {
+               dmi_write(target, DMI_DMCONTROL, 0);
+               dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
+               dm->was_reset = true;
+       }
+
+       dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO |
+                       DMI_DMCONTROL_HARTSELHI | DMI_DMCONTROL_DMACTIVE);
+       uint32_t dmcontrol;
+       if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
+               return ERROR_FAIL;
+
+       if (!get_field(dmcontrol, DMI_DMCONTROL_DMACTIVE)) {
+               LOG_ERROR("Debug Module did not become active. dmcontrol=0x%x",
+                               dmcontrol);
+               return ERROR_FAIL;
+       }
+
+       uint32_t hartsel =
+               (get_field(dmcontrol, DMI_DMCONTROL_HARTSELHI) <<
+                DMI_DMCONTROL_HARTSELLO_LENGTH) |
+               get_field(dmcontrol, DMI_DMCONTROL_HARTSELLO);
+       info->hartsellen = 0;
+       while (hartsel & 1) {
+               info->hartsellen++;
+               hartsel >>= 1;
+       }
+       LOG_DEBUG("hartsellen=%d", info->hartsellen);
+
+       uint32_t hartinfo;
+       if (dmi_read(target, &hartinfo, DMI_HARTINFO) != ERROR_OK)
+               return ERROR_FAIL;
+
+       info->datasize = get_field(hartinfo, DMI_HARTINFO_DATASIZE);
+       info->dataaccess = get_field(hartinfo, DMI_HARTINFO_DATAACCESS);
+       info->dataaddr = get_field(hartinfo, DMI_HARTINFO_DATAADDR);
+
+       if (!get_field(dmstatus, DMI_DMSTATUS_AUTHENTICATED)) {
+               LOG_ERROR("Debugger is not authenticated to target Debug Module. "
+                               "(dmstatus=0x%x). Use `riscv authdata_read` and "
+                               "`riscv authdata_write` commands to authenticate.", dmstatus);
+               /* If we return ERROR_FAIL here, then in a multicore setup the next
+                * core won't be examined, which means we won't set up the
+                * authentication commands for them, which means the config script
+                * needs to be a lot more complex. */
+               return ERROR_OK;
+       }
+
+       if (dmi_read(target, &info->sbcs, DMI_SBCS) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* Check that abstract data registers are accessible. */
+       uint32_t abstractcs;
+       if (dmi_read(target, &abstractcs, DMI_ABSTRACTCS) != ERROR_OK)
+               return ERROR_FAIL;
+       info->datacount = get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT);
+       info->progbufsize = get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE);
+
+       LOG_INFO("datacount=%d progbufsize=%d", info->datacount, info->progbufsize);
+
+       RISCV_INFO(r);
+       r->impebreak = get_field(dmstatus, DMI_DMSTATUS_IMPEBREAK);
+
+       if (info->progbufsize + r->impebreak < 2) {
+               LOG_WARNING("We won't be able to execute fence instructions on this "
+                               "target. Memory may not always appear consistent. "
+                               "(progbufsize=%d, impebreak=%d)", info->progbufsize,
+                               r->impebreak);
+       }
+
+       /* Before doing anything else we must first enumerate the harts. */
+
+       /* Don't call any riscv_* functions until after we've counted the number of
+        * cores and initialized registers. */
+       for (int i = 0; i < MIN(RISCV_MAX_HARTS, 1 << info->hartsellen); ++i) {
+               if (!riscv_rtos_enabled(target) && i != target->coreid)
+                       continue;
+
+               r->current_hartid = i;
+               if (riscv013_select_current_hart(target) != ERROR_OK)
+                       return ERROR_FAIL;
+
+               uint32_t s;
+               if (dmstatus_read(target, &s, true) != ERROR_OK)
+                       return ERROR_FAIL;
+               if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT))
+                       break;
+               r->hart_count = i + 1;
+
+               if (get_field(s, DMI_DMSTATUS_ANYHAVERESET))
+                       dmi_write(target, DMI_DMCONTROL,
+                                       set_hartsel(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET, i));
+
+               if (!riscv_is_halted(target)) {
+                       if (riscv013_halt_current_hart(target) != ERROR_OK) {
+                               LOG_ERROR("Fatal: Hart %d failed to halt during examine()", i);
+                               return ERROR_FAIL;
+                       }
+               }
+
+               /* Without knowing anything else we can at least mess with the
+                * program buffer. */
+               r->debug_buffer_size[i] = info->progbufsize;
+
+               int result = register_read_abstract(target, NULL, GDB_REGNO_S0, 64);
+               if (result == ERROR_OK)
+                       r->xlen[i] = 64;
+               else
+                       r->xlen[i] = 32;
+
+               if (register_read(target, &r->misa[i], GDB_REGNO_MISA)) {
+                       LOG_ERROR("Fatal: Failed to read MISA from hart %d.", i);
+                       return ERROR_FAIL;
+               }
+
+               /* Now init registers based on what we discovered. */
+               if (riscv_init_registers(target) != ERROR_OK)
+                       return ERROR_FAIL;
+
+               /* Display this as early as possible to help people who are using
+                * really slow simulators. */
+               LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i],
+                               r->misa[i]);
+       }
+
+       LOG_DEBUG("Enumerated %d harts", r->hart_count);
+
+       if (r->hart_count == 0) {
+               LOG_ERROR("No harts found!");
+               return ERROR_FAIL;
+       }
+
+       /* Resumes all the harts, so the debugger can later pause them. */
+       /* TODO: Only do this if the harts were halted to start with. */
+       riscv_resume_all_harts(target);
+       target->state = TARGET_RUNNING;
+
+       target_set_examined(target);
+
+       /* Some regression suites rely on seeing 'Examined RISC-V core' to know
+        * when they can connect with gdb/telnet.
+        * We will need to update those suites if we want to change that text. */
+       LOG_INFO("Examined RISC-V core; found %d harts",
+                       riscv_count_harts(target));
+       for (int i = 0; i < riscv_count_harts(target); ++i) {
+               if (riscv_hart_enabled(target, i)) {
+                       LOG_INFO(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i],
+                                       r->misa[i]);
+               } else {
+                       LOG_INFO(" hart %d: currently disabled", i);
+               }
+       }
+       return ERROR_OK;
+}
+
+int riscv013_authdata_read(struct target *target, uint32_t *value)
+{
+       if (wait_for_authbusy(target, NULL) != ERROR_OK)
+               return ERROR_FAIL;
+
+       return dmi_read(target, value, DMI_AUTHDATA);
+}
+
+int riscv013_authdata_write(struct target *target, uint32_t value)
+{
+       uint32_t before, after;
+       if (wait_for_authbusy(target, &before) != ERROR_OK)
+               return ERROR_FAIL;
+
+       dmi_write(target, DMI_AUTHDATA, value);
+
+       if (wait_for_authbusy(target, &after) != ERROR_OK)
+               return ERROR_FAIL;
+
+       if (!get_field(before, DMI_DMSTATUS_AUTHENTICATED) &&
+                       get_field(after, DMI_DMSTATUS_AUTHENTICATED)) {
+               LOG_INFO("authdata_write resulted in successful authentication");
+               int result = ERROR_OK;
+               dm013_info_t *dm = get_dm(target);
+               target_list_t *entry;
+               list_for_each_entry(entry, &dm->target_list, list) {
+                       if (examine(entry->target) != ERROR_OK)
+                               result = ERROR_FAIL;
+               }
+               return result;
+       }
+
+       return ERROR_OK;
+}
+
+static int init_target(struct command_context *cmd_ctx,
+               struct target *target)
+{
+       LOG_DEBUG("init");
+       riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
+
+       generic_info->get_register = &riscv013_get_register;
+       generic_info->set_register = &riscv013_set_register;
+       generic_info->select_current_hart = &riscv013_select_current_hart;
+       generic_info->is_halted = &riscv013_is_halted;
+       generic_info->halt_current_hart = &riscv013_halt_current_hart;
+       generic_info->resume_current_hart = &riscv013_resume_current_hart;
+       generic_info->step_current_hart = &riscv013_step_current_hart;
+       generic_info->on_halt = &riscv013_on_halt;
+       generic_info->on_resume = &riscv013_on_resume;
+       generic_info->on_step = &riscv013_on_step;
+       generic_info->halt_reason = &riscv013_halt_reason;
+       generic_info->read_debug_buffer = &riscv013_read_debug_buffer;
+       generic_info->write_debug_buffer = &riscv013_write_debug_buffer;
+       generic_info->execute_debug_buffer = &riscv013_execute_debug_buffer;
+       generic_info->fill_dmi_write_u64 = &riscv013_fill_dmi_write_u64;
+       generic_info->fill_dmi_read_u64 = &riscv013_fill_dmi_read_u64;
+       generic_info->fill_dmi_nop_u64 = &riscv013_fill_dmi_nop_u64;
+       generic_info->dmi_write_u64_bits = &riscv013_dmi_write_u64_bits;
+       generic_info->authdata_read = &riscv013_authdata_read;
+       generic_info->authdata_write = &riscv013_authdata_write;
+       generic_info->dmi_read = &dmi_read;
+       generic_info->dmi_write = &dmi_write;
+       generic_info->version_specific = calloc(1, sizeof(riscv013_info_t));
+       if (!generic_info->version_specific)
+               return ERROR_FAIL;
+       riscv013_info_t *info = get_info(target);
+
+       info->progbufsize = -1;
+
+       info->dmi_busy_delay = 0;
+       info->bus_master_read_delay = 0;
+       info->bus_master_write_delay = 0;
+       info->ac_busy_delay = 0;
+
+       /* Assume all these abstract commands are supported until we learn
+        * otherwise.
+        * TODO: The spec allows eg. one CSR to be able to be accessed abstractly
+        * while another one isn't. We don't track that this closely here, but in
+        * the future we probably should. */
+       info->abstract_read_csr_supported = true;
+       info->abstract_write_csr_supported = true;
+       info->abstract_read_fpr_supported = true;
+       info->abstract_write_fpr_supported = true;
+
+       return ERROR_OK;
+}
+
+static int assert_reset(struct target *target)
+{
+       RISCV_INFO(r);
+
+       select_dmi(target);
+
+       uint32_t control_base = set_field(0, DMI_DMCONTROL_DMACTIVE, 1);
+
+       if (target->rtos) {
+               /* There's only one target, and OpenOCD thinks each hart is a thread.
+                * We must reset them all. */
+
+               /* TODO: Try to use hasel in dmcontrol */
+
+               /* Set haltreq for each hart. */
+               uint32_t control = control_base;
+               for (int i = 0; i < riscv_count_harts(target); ++i) {
+                       if (!riscv_hart_enabled(target, i))
+                               continue;
+
+                       control = set_hartsel(control_base, i);
+                       control = set_field(control, DMI_DMCONTROL_HALTREQ,
+                                       target->reset_halt ? 1 : 0);
+                       dmi_write(target, DMI_DMCONTROL, control);
+               }
+               /* Assert ndmreset */
+               control = set_field(control, DMI_DMCONTROL_NDMRESET, 1);
+               dmi_write(target, DMI_DMCONTROL, control);
+
+       } else {
+               /* Reset just this hart. */
+               uint32_t control = set_hartsel(control_base, r->current_hartid);
+               control = set_field(control, DMI_DMCONTROL_HALTREQ,
+                               target->reset_halt ? 1 : 0);
+               control = set_field(control, DMI_DMCONTROL_NDMRESET, 1);
+               dmi_write(target, DMI_DMCONTROL, control);
+       }
+
+       target->state = TARGET_RESET;
+
+       return ERROR_OK;
+}
+
+static int deassert_reset(struct target *target)
+{
+       RISCV_INFO(r);
+       RISCV013_INFO(info);
+       select_dmi(target);
+
+       /* Clear the reset, but make sure haltreq is still set */
+       uint32_t control = 0;
+       control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
+       control = set_field(control, DMI_DMCONTROL_DMACTIVE, 1);
+       dmi_write(target, DMI_DMCONTROL,
+                       set_hartsel(control, r->current_hartid));
+
+       uint32_t dmstatus;
+       int dmi_busy_delay = info->dmi_busy_delay;
+       time_t start = time(NULL);
+
+       for (int i = 0; i < riscv_count_harts(target); ++i) {
+               int index = i;
+               if (target->rtos) {
+                       if (!riscv_hart_enabled(target, index))
+                               continue;
+                       dmi_write(target, DMI_DMCONTROL,
+                                       set_hartsel(control, index));
+               } else {
+                       index = r->current_hartid;
+               }
+
+               char *operation;
+               uint32_t expected_field;
+               if (target->reset_halt) {
+                       operation = "halt";
+                       expected_field = DMI_DMSTATUS_ALLHALTED;
+               } else {
+                       operation = "run";
+                       expected_field = DMI_DMSTATUS_ALLRUNNING;
+               }
+               LOG_DEBUG("Waiting for hart %d to %s out of reset.", index, operation);
+               while (1) {
+                       int result = dmstatus_read_timeout(target, &dmstatus, true,
+                                       riscv_reset_timeout_sec);
+                       if (result == ERROR_TIMEOUT_REACHED)
+                               LOG_ERROR("Hart %d didn't complete a DMI read coming out of "
+                                               "reset in %ds; Increase the timeout with riscv "
+                                               "set_reset_timeout_sec.",
+                                               index, riscv_reset_timeout_sec);
+                       if (result != ERROR_OK)
+                               return result;
+                       if (get_field(dmstatus, expected_field))
+                               break;
+                       if (time(NULL) - start > riscv_reset_timeout_sec) {
+                               LOG_ERROR("Hart %d didn't %s coming out of reset in %ds; "
+                                               "dmstatus=0x%x; "
+                                               "Increase the timeout with riscv set_reset_timeout_sec.",
+                                               index, operation, riscv_reset_timeout_sec, dmstatus);
+                               return ERROR_FAIL;
+                       }
+               }
+               target->state = TARGET_HALTED;
+
+               if (get_field(dmstatus, DMI_DMSTATUS_ALLHAVERESET)) {
+                       /* Ack reset. */
+                       dmi_write(target, DMI_DMCONTROL,
+                                       set_hartsel(control, index) |
+                                       DMI_DMCONTROL_ACKHAVERESET);
+               }
+
+               if (!target->rtos)
+                       break;
+       }
+       info->dmi_busy_delay = dmi_busy_delay;
+       return ERROR_OK;
+}
+
+/**
+ * @size in bytes
+ */
+static void write_to_buf(uint8_t *buffer, uint64_t value, unsigned size)
+{
+       switch (size) {
+               case 8:
+                       buffer[7] = value >> 56;
+                       buffer[6] = value >> 48;
+                       buffer[5] = value >> 40;
+                       buffer[4] = value >> 32;
+                       /* falls through */
+               case 4:
+                       buffer[3] = value >> 24;
+                       buffer[2] = value >> 16;
+                       /* falls through */
+               case 2:
+                       buffer[1] = value >> 8;
+                       /* falls through */
+               case 1:
+                       buffer[0] = value;
+                       break;
+               default:
+                       assert(false);
+       }
+}
+
+static int execute_fence(struct target *target)
+{
+       struct riscv_program program;
+       riscv_program_init(&program, target);
+       riscv_program_fence(&program);
+       int result = riscv_program_exec(&program, target);
+       if (result != ERROR_OK)
+               LOG_ERROR("Unable to execute fence");
+       return result;
+}
+
+static void log_memory_access(target_addr_t address, uint64_t value,
+               unsigned size_bytes, bool read)
+{
+       if (debug_level < LOG_LVL_DEBUG)
+               return;
+
+       char fmt[80];
+       sprintf(fmt, "M[0x%" TARGET_PRIxADDR "] %ss 0x%%0%d" PRIx64,
+                       address, read ? "read" : "write", size_bytes * 2);
+       value &= (((uint64_t) 0x1) << (size_bytes * 8)) - 1;
+       LOG_DEBUG(fmt, value);
+}
+
+/* Read the relevant sbdata regs depending on size, and put the results into
+ * buffer. */
+static int read_memory_bus_word(struct target *target, target_addr_t address,
+               uint32_t size, uint8_t *buffer)
+{
+       uint32_t value;
+       if (size > 12) {
+               if (dmi_read(target, &value, DMI_SBDATA3) != ERROR_OK)
+                       return ERROR_FAIL;
+               write_to_buf(buffer + 12, value, 4);
+               log_memory_access(address + 12, value, 4, true);
+       }
+       if (size > 8) {
+               if (dmi_read(target, &value, DMI_SBDATA2) != ERROR_OK)
+                       return ERROR_FAIL;
+               write_to_buf(buffer + 8, value, 4);
+               log_memory_access(address + 8, value, 4, true);
+       }
+       if (size > 4) {
+               if (dmi_read(target, &value, DMI_SBDATA1) != ERROR_OK)
+                       return ERROR_FAIL;
+               write_to_buf(buffer + 4, value, 4);
+               log_memory_access(address + 4, value, 4, true);
+       }
+       if (dmi_read(target, &value, DMI_SBDATA0) != ERROR_OK)
+               return ERROR_FAIL;
+       write_to_buf(buffer, value, MIN(size, 4));
+       log_memory_access(address, value, MIN(size, 4), true);
+       return ERROR_OK;
+}
+
+static uint32_t sb_sbaccess(unsigned size_bytes)
+{
+       switch (size_bytes) {
+               case 1:
+                       return set_field(0, DMI_SBCS_SBACCESS, 0);
+               case 2:
+                       return set_field(0, DMI_SBCS_SBACCESS, 1);
+               case 4:
+                       return set_field(0, DMI_SBCS_SBACCESS, 2);
+               case 8:
+                       return set_field(0, DMI_SBCS_SBACCESS, 3);
+               case 16:
+                       return set_field(0, DMI_SBCS_SBACCESS, 4);
+       }
+       assert(0);
+       return 0;       /* Make mingw happy. */
+}
+
+static target_addr_t sb_read_address(struct target *target)
+{
+       RISCV013_INFO(info);
+       unsigned sbasize = get_field(info->sbcs, DMI_SBCS_SBASIZE);
+       target_addr_t address = 0;
+       uint32_t v;
+       if (sbasize > 32) {
+#if BUILD_TARGET64
+               dmi_read(target, &v, DMI_SBADDRESS1);
+               address |= v;
+               address <<= 32;
+#endif
+       }
+       dmi_read(target, &v, DMI_SBADDRESS0);
+       address |= v;
+       return address;
+}
+
+static int sb_write_address(struct target *target, target_addr_t address)
+{
+       RISCV013_INFO(info);
+       unsigned sbasize = get_field(info->sbcs, DMI_SBCS_SBASIZE);
+       /* There currently is no support for >64-bit addresses in OpenOCD. */
+       if (sbasize > 96)
+               dmi_write(target, DMI_SBADDRESS3, 0);
+       if (sbasize > 64)
+               dmi_write(target, DMI_SBADDRESS2, 0);
+       if (sbasize > 32)
+#if BUILD_TARGET64
+               dmi_write(target, DMI_SBADDRESS1, address >> 32);
+#else
+               dmi_write(target, DMI_SBADDRESS1, 0);
+#endif
+       return dmi_write(target, DMI_SBADDRESS0, address);
+}
+
+static int read_sbcs_nonbusy(struct target *target, uint32_t *sbcs)
+{
+       time_t start = time(NULL);
+       while (1) {
+               if (dmi_read(target, sbcs, DMI_SBCS) != ERROR_OK)
+                       return ERROR_FAIL;
+               if (!get_field(*sbcs, DMI_SBCS_SBBUSY))
+                       return ERROR_OK;
+               if (time(NULL) - start > riscv_command_timeout_sec) {
+                       LOG_ERROR("Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). "
+                                       "Increase the timeout with riscv set_command_timeout_sec.",
+                                       riscv_command_timeout_sec, *sbcs);
+                       return ERROR_FAIL;
+               }
+       }
+}
+
+static int read_memory_bus_v0(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, uint8_t *buffer)
+{
+       LOG_DEBUG("System Bus Access: size: %d\tcount:%d\tstart address: 0x%08"
+                       TARGET_PRIxADDR, size, count, address);
+       uint8_t *t_buffer = buffer;
+       riscv_addr_t cur_addr = address;
+       riscv_addr_t fin_addr = address + (count * size);
+       uint32_t access = 0;
+
+       const int DMI_SBCS_SBSINGLEREAD_OFFSET = 20;
+       const uint32_t DMI_SBCS_SBSINGLEREAD = (0x1U << DMI_SBCS_SBSINGLEREAD_OFFSET);
+
+       const int DMI_SBCS_SBAUTOREAD_OFFSET = 15;
+       const uint32_t DMI_SBCS_SBAUTOREAD = (0x1U << DMI_SBCS_SBAUTOREAD_OFFSET);
+
+       /* ww favorise one off reading if there is an issue */
+       if (count == 1) {
+               for (uint32_t i = 0; i < count; i++) {
+                       if (dmi_read(target, &access, DMI_SBCS) != ERROR_OK)
+                               return ERROR_FAIL;
+                       dmi_write(target, DMI_SBADDRESS0, cur_addr);
+                       /* size/2 matching the bit access of the spec 0.13 */
+                       access = set_field(access, DMI_SBCS_SBACCESS, size/2);
+                       access = set_field(access, DMI_SBCS_SBSINGLEREAD, 1);
+                       LOG_DEBUG("\r\nread_memory: sab: access:  0x%08x", access);
+                       dmi_write(target, DMI_SBCS, access);
+                       /* 3) read */
+                       uint32_t value;
+                       if (dmi_read(target, &value, DMI_SBDATA0) != ERROR_OK)
+                               return ERROR_FAIL;
+                       LOG_DEBUG("\r\nread_memory: sab: value:  0x%08x", value);
+                       write_to_buf(t_buffer, value, size);
+                       t_buffer += size;
+                       cur_addr += size;
+               }
+               return ERROR_OK;
+       }
+
+       /* has to be the same size if we want to read a block */
+       LOG_DEBUG("reading block until final address 0x%" PRIx64, fin_addr);
+       if (dmi_read(target, &access, DMI_SBCS) != ERROR_OK)
+               return ERROR_FAIL;
+       /* set current address */
+       dmi_write(target, DMI_SBADDRESS0, cur_addr);
+       /* 2) write sbaccess=2, sbsingleread,sbautoread,sbautoincrement
+        * size/2 matching the bit access of the spec 0.13 */
+       access = set_field(access, DMI_SBCS_SBACCESS, size/2);
+       access = set_field(access, DMI_SBCS_SBAUTOREAD, 1);
+       access = set_field(access, DMI_SBCS_SBSINGLEREAD, 1);
+       access = set_field(access, DMI_SBCS_SBAUTOINCREMENT, 1);
+       LOG_DEBUG("\r\naccess:  0x%08x", access);
+       dmi_write(target, DMI_SBCS, access);
+
+       while (cur_addr < fin_addr) {
+               LOG_DEBUG("\r\nsab:autoincrement: \r\n size: %d\tcount:%d\taddress: 0x%08"
+                               PRIx64, size, count, cur_addr);
+               /* read */
+               uint32_t value;
+               if (dmi_read(target, &value, DMI_SBDATA0) != ERROR_OK)
+                       return ERROR_FAIL;
+               write_to_buf(t_buffer, value, size);
+               cur_addr += size;
+               t_buffer += size;
+
+               /* if we are reaching last address, we must clear autoread */
+               if (cur_addr == fin_addr && count != 1) {
+                       dmi_write(target, DMI_SBCS, 0);
+                       if (dmi_read(target, &value, DMI_SBDATA0) != ERROR_OK)
+                               return ERROR_FAIL;
+                       write_to_buf(t_buffer, value, size);
+               }
+       }
+
+       return ERROR_OK;
+}
+
+/**
+ * Read the requested memory using the system bus interface.
+ */
+static int read_memory_bus_v1(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, uint8_t *buffer)
+{
+       RISCV013_INFO(info);
+       target_addr_t next_address = address;
+       target_addr_t end_address = address + count * size;
+
+       while (next_address < end_address) {
+               uint32_t sbcs = set_field(0, DMI_SBCS_SBREADONADDR, 1);
+               sbcs |= sb_sbaccess(size);
+               sbcs = set_field(sbcs, DMI_SBCS_SBAUTOINCREMENT, 1);
+               sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, count > 1);
+               dmi_write(target, DMI_SBCS, sbcs);
+
+               /* This address write will trigger the first read. */
+               sb_write_address(target, next_address);
+
+               if (info->bus_master_read_delay) {
+                       jtag_add_runtest(info->bus_master_read_delay, TAP_IDLE);
+                       if (jtag_execute_queue() != ERROR_OK) {
+                               LOG_ERROR("Failed to scan idle sequence");
+                               return ERROR_FAIL;
+                       }
+               }
+
+               for (uint32_t i = (next_address - address) / size; i < count - 1; i++) {
+                       read_memory_bus_word(target, address + i * size, size,
+                                       buffer + i * size);
+               }
+
+               sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, 0);
+               dmi_write(target, DMI_SBCS, sbcs);
+
+               read_memory_bus_word(target, address + (count - 1) * size, size,
+                               buffer + (count - 1) * size);
+
+               if (read_sbcs_nonbusy(target, &sbcs) != ERROR_OK)
+                       return ERROR_FAIL;
+
+               if (get_field(sbcs, DMI_SBCS_SBBUSYERROR)) {
+                       /* We read while the target was busy. Slow down and try again. */
+                       dmi_write(target, DMI_SBCS, DMI_SBCS_SBBUSYERROR);
+                       next_address = sb_read_address(target);
+                       info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1;
+                       continue;
+               }
+
+               unsigned error = get_field(sbcs, DMI_SBCS_SBERROR);
+               if (error == 0) {
+                       next_address = end_address;
+               } else {
+                       /* Some error indicating the bus access failed, but not because of
+                        * something we did wrong. */
+                       dmi_write(target, DMI_SBCS, DMI_SBCS_SBERROR);
+                       return ERROR_FAIL;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+/**
+ * Read the requested memory, taking care to execute every read exactly once,
+ * even if cmderr=busy is encountered.
+ */
+static int read_memory_progbuf(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, uint8_t *buffer)
+{
+       RISCV013_INFO(info);
+
+       int result = ERROR_OK;
+
+       LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
+                       size, address);
+
+       select_dmi(target);
+
+       /* s0 holds the next address to write to
+        * s1 holds the next data value to write
+        */
+       uint64_t s0, s1;
+       if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+               return ERROR_FAIL;
+       if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK)
+               return ERROR_FAIL;
+
+       if (execute_fence(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* Write the program (load, increment) */
+       struct riscv_program program;
+       riscv_program_init(&program, target);
+       switch (size) {
+               case 1:
+                       riscv_program_lbr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+                       break;
+               case 2:
+                       riscv_program_lhr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+                       break;
+               case 4:
+                       riscv_program_lwr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+                       break;
+               default:
+                       LOG_ERROR("Unsupported size: %d", size);
+                       return ERROR_FAIL;
+       }
+       riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size);
+
+       if (riscv_program_ebreak(&program) != ERROR_OK)
+               return ERROR_FAIL;
+       riscv_program_write(&program);
+
+       /* Write address to S0, and execute buffer. */
+       result = register_write_direct(target, GDB_REGNO_S0, address);
+       if (result != ERROR_OK)
+               goto error;
+       uint32_t command = access_register_command(GDB_REGNO_S1, riscv_xlen(target),
+                               AC_ACCESS_REGISTER_TRANSFER |
+                               AC_ACCESS_REGISTER_POSTEXEC);
+       result = execute_abstract_command(target, command);
+       if (result != ERROR_OK)
+               goto error;
+
+       /* First read has just triggered. Result is in s1. */
+
+       dmi_write(target, DMI_ABSTRACTAUTO,
+                       1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET);
+
+       /* read_addr is the next address that the hart will read from, which is the
+        * value in s0. */
+       riscv_addr_t read_addr = address + size;
+       /* The next address that we need to receive data for. */
+       riscv_addr_t receive_addr = address;
+       riscv_addr_t fin_addr = address + (count * size);
+       unsigned skip = 1;
+       while (read_addr < fin_addr) {
+               LOG_DEBUG("read_addr=0x%" PRIx64 ", receive_addr=0x%" PRIx64
+                               ", fin_addr=0x%" PRIx64, read_addr, receive_addr, fin_addr);
+               /* The pipeline looks like this:
+                * memory -> s1 -> dm_data0 -> debugger
+                * It advances every time the debugger reads dmdata0.
+                * So at any time the debugger has just read mem[s0 - 3*size],
+                * dm_data0 contains mem[s0 - 2*size]
+                * s1 contains mem[s0-size] */
+
+               LOG_DEBUG("creating burst to read from 0x%" PRIx64
+                               " up to 0x%" PRIx64, read_addr, fin_addr);
+               assert(read_addr >= address && read_addr < fin_addr);
+               struct riscv_batch *batch = riscv_batch_alloc(target, 32,
+                               info->dmi_busy_delay + info->ac_busy_delay);
+
+               size_t reads = 0;
+               for (riscv_addr_t addr = read_addr; addr < fin_addr; addr += size) {
+                       riscv_batch_add_dmi_read(batch, DMI_DATA0);
+
+                       reads++;
+                       if (riscv_batch_full(batch))
+                               break;
+               }
+
+               riscv_batch_run(batch);
+
+               /* Wait for the target to finish performing the last abstract command,
+                * and update our copy of cmderr. */
+               uint32_t abstractcs;
+               if (dmi_read(target, &abstractcs, DMI_ABSTRACTCS) != ERROR_OK)
+                       return ERROR_FAIL;
+               while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY))
+                       if (dmi_read(target, &abstractcs, DMI_ABSTRACTCS) != ERROR_OK)
+                               return ERROR_FAIL;
+               info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR);
+
+               unsigned cmderr = info->cmderr;
+               riscv_addr_t next_read_addr;
+               uint32_t dmi_data0 = -1;
+               switch (info->cmderr) {
+                       case CMDERR_NONE:
+                               LOG_DEBUG("successful (partial?) memory read");
+                               next_read_addr = read_addr + reads * size;
+                               break;
+                       case CMDERR_BUSY:
+                               LOG_DEBUG("memory read resulted in busy response");
+
+                               /*
+                                * If you want to exercise this code path, apply the following patch to spike:
+--- a/riscv/debug_module.cc
++++ b/riscv/debug_module.cc
+@@ -1,3 +1,5 @@
++#include <unistd.h>
++
+ #include <cassert>
+
+ #include "debug_module.h"
+@@ -398,6 +400,15 @@ bool debug_module_t::perform_abstract_command()
+       // Since the next instruction is what we will use, just use nother NOP
+       // to get there.
+       write32(debug_abstract, 1, addi(ZERO, ZERO, 0));
++
++      if (abstractauto.autoexecdata &&
++          program_buffer[0] == 0x83 &&
++          program_buffer[1] == 0x24 &&
++          program_buffer[2] == 0x04 &&
++          program_buffer[3] == 0 &&
++          rand() < RAND_MAX / 10) {
++        usleep(1000000);
++      }
+     } else {
+       write32(debug_abstract, 1, ebreak());
+     }
+                                */
+                               increase_ac_busy_delay(target);
+                               riscv013_clear_abstract_error(target);
+
+                               dmi_write(target, DMI_ABSTRACTAUTO, 0);
+
+                               /* This is definitely a good version of the value that we
+                                * attempted to read when we discovered that the target was
+                                * busy. */
+                               if (dmi_read(target, &dmi_data0, DMI_DATA0) != ERROR_OK) {
+                                       riscv_batch_free(batch);
+                                       goto error;
+                               }
+
+                               /* Clobbers DMI_DATA0. */
+                               result = register_read_direct(target, &next_read_addr,
+                                               GDB_REGNO_S0);
+                               if (result != ERROR_OK) {
+                                       riscv_batch_free(batch);
+                                       goto error;
+                               }
+                               /* Restore the command, and execute it.
+                                * Now DMI_DATA0 contains the next value just as it would if no
+                                * error had occurred. */
+                               dmi_write(target, DMI_COMMAND, command);
+
+                               dmi_write(target, DMI_ABSTRACTAUTO,
+                                               1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET);
+                               break;
+                       default:
+                               LOG_ERROR("error when reading memory, abstractcs=0x%08lx", (long)abstractcs);
+                               riscv013_clear_abstract_error(target);
+                               riscv_batch_free(batch);
+                               result = ERROR_FAIL;
+                               goto error;
+               }
+
+               /* Now read whatever we got out of the batch. */
+               for (size_t i = 0; i < reads; i++) {
+                       if (read_addr >= next_read_addr)
+                               break;
+
+                       read_addr += size;
+
+                       if (skip > 0) {
+                               skip--;
+                               continue;
+                       }
+
+                       riscv_addr_t offset = receive_addr - address;
+                       uint64_t dmi_out = riscv_batch_get_dmi_read(batch, i);
+                       uint32_t value = get_field(dmi_out, DTM_DMI_DATA);
+                       write_to_buf(buffer + offset, value, size);
+                       log_memory_access(receive_addr, value, size, true);
+
+                       receive_addr += size;
+               }
+               riscv_batch_free(batch);
+
+               if (cmderr == CMDERR_BUSY) {
+                       riscv_addr_t offset = receive_addr - address;
+                       write_to_buf(buffer + offset, dmi_data0, size);
+                       log_memory_access(receive_addr, dmi_data0, size, true);
+                       read_addr += size;
+                       receive_addr += size;
+               }
+       }
+
+       dmi_write(target, DMI_ABSTRACTAUTO, 0);
+
+       if (count > 1) {
+               /* Read the penultimate word. */
+               uint32_t value;
+               if (dmi_read(target, &value, DMI_DATA0) != ERROR_OK)
+                       goto error;
+               write_to_buf(buffer + receive_addr - address, value, size);
+               log_memory_access(receive_addr, value, size, true);
+               receive_addr += size;
+       }
+
+       /* Read the last word. */
+       uint64_t value;
+       result = register_read_direct(target, &value, GDB_REGNO_S1);
+       if (result != ERROR_OK)
+               goto error;
+       write_to_buf(buffer + receive_addr - address, value, size);
+       log_memory_access(receive_addr, value, size, true);
+
+       riscv_set_register(target, GDB_REGNO_S0, s0);
+       riscv_set_register(target, GDB_REGNO_S1, s1);
+       return ERROR_OK;
+
+error:
+       dmi_write(target, DMI_ABSTRACTAUTO, 0);
+
+       riscv_set_register(target, GDB_REGNO_S0, s0);
+       riscv_set_register(target, GDB_REGNO_S1, s1);
+       return result;
+}
+
+static int read_memory(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, uint8_t *buffer)
+{
+       RISCV013_INFO(info);
+       if (info->progbufsize >= 2 && !riscv_prefer_sba)
+               return read_memory_progbuf(target, address, size, count, buffer);
+
+       if ((get_field(info->sbcs, DMI_SBCS_SBACCESS8) && size == 1) ||
+                       (get_field(info->sbcs, DMI_SBCS_SBACCESS16) && size == 2) ||
+                       (get_field(info->sbcs, DMI_SBCS_SBACCESS32) && size == 4) ||
+                       (get_field(info->sbcs, DMI_SBCS_SBACCESS64) && size == 8) ||
+                       (get_field(info->sbcs, DMI_SBCS_SBACCESS128) && size == 16)) {
+               if (get_field(info->sbcs, DMI_SBCS_SBVERSION) == 0)
+                       return read_memory_bus_v0(target, address, size, count, buffer);
+               else if (get_field(info->sbcs, DMI_SBCS_SBVERSION) == 1)
+                       return read_memory_bus_v1(target, address, size, count, buffer);
+       }
+
+       if (info->progbufsize >= 2)
+               return read_memory_progbuf(target, address, size, count, buffer);
+
+       LOG_ERROR("Don't know how to read memory on this target.");
+       return ERROR_FAIL;
+}
+
+static int write_memory_bus_v0(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+       /*1) write sbaddress: for singlewrite and autoincrement, we need to write the address once*/
+       LOG_DEBUG("System Bus Access: size: %d\tcount:%d\tstart address: 0x%08"
+                       TARGET_PRIxADDR, size, count, address);
+       dmi_write(target, DMI_SBADDRESS0, address);
+       int64_t value = 0;
+       int64_t access = 0;
+       riscv_addr_t offset = 0;
+       riscv_addr_t t_addr = 0;
+       const uint8_t *t_buffer = buffer + offset;
+
+       /* B.8 Writing Memory, single write check if we write in one go */
+       if (count == 1) { /* count is in bytes here */
+               /* check the size */
+               switch (size) {
+                       case 1:
+                               value = t_buffer[0];
+                               break;
+                       case 2:
+                               value = t_buffer[0]
+                                       | ((uint32_t) t_buffer[1] << 8);
+                               break;
+                       case 4:
+                               value = t_buffer[0]
+                                       | ((uint32_t) t_buffer[1] << 8)
+                                       | ((uint32_t) t_buffer[2] << 16)
+                                       | ((uint32_t) t_buffer[3] << 24);
+                               break;
+                       default:
+                               LOG_ERROR("unsupported access size: %d", size);
+                               return ERROR_FAIL;
+               }
+
+               access = 0;
+               access = set_field(access, DMI_SBCS_SBACCESS, size/2);
+               dmi_write(target, DMI_SBCS, access);
+               LOG_DEBUG("\r\naccess:  0x%08" PRIx64, access);
+               LOG_DEBUG("\r\nwrite_memory:SAB: ONE OFF: value 0x%08" PRIx64, value);
+               dmi_write(target, DMI_SBDATA0, value);
+               return ERROR_OK;
+       }
+
+       /*B.8 Writing Memory, using autoincrement*/
+
+       access = 0;
+       access = set_field(access, DMI_SBCS_SBACCESS, size/2);
+       access = set_field(access, DMI_SBCS_SBAUTOINCREMENT, 1);
+       LOG_DEBUG("\r\naccess:  0x%08" PRIx64, access);
+       dmi_write(target, DMI_SBCS, access);
+
+       /*2)set the value according to the size required and write*/
+       for (riscv_addr_t i = 0; i < count; ++i) {
+               offset = size*i;
+               /* for monitoring only */
+               t_addr = address + offset;
+               t_buffer = buffer + offset;
+
+               switch (size) {
+                       case 1:
+                               value = t_buffer[0];
+                               break;
+                       case 2:
+                               value = t_buffer[0]
+                                       | ((uint32_t) t_buffer[1] << 8);
+                               break;
+                       case 4:
+                               value = t_buffer[0]
+                                       | ((uint32_t) t_buffer[1] << 8)
+                                       | ((uint32_t) t_buffer[2] << 16)
+                                       | ((uint32_t) t_buffer[3] << 24);
+                               break;
+                       default:
+                               LOG_ERROR("unsupported access size: %d", size);
+                               return ERROR_FAIL;
+               }
+               LOG_DEBUG("SAB:autoincrement: expected address: 0x%08x value: 0x%08x"
+                               PRIx64, (uint32_t)t_addr, (uint32_t)value);
+               dmi_write(target, DMI_SBDATA0, value);
+       }
+       /*reset the autoincrement when finished (something weird is happening if this is not done at the end*/
+       access = set_field(access, DMI_SBCS_SBAUTOINCREMENT, 0);
+       dmi_write(target, DMI_SBCS, access);
+
+       return ERROR_OK;
+}
+
+static int write_memory_bus_v1(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+       RISCV013_INFO(info);
+       uint32_t sbcs = sb_sbaccess(size);
+       sbcs = set_field(sbcs, DMI_SBCS_SBAUTOINCREMENT, 1);
+       dmi_write(target, DMI_SBCS, sbcs);
+
+       target_addr_t next_address = address;
+       target_addr_t end_address = address + count * size;
+
+       sb_write_address(target, next_address);
+       while (next_address < end_address) {
+               for (uint32_t i = (next_address - address) / size; i < count; i++) {
+                       const uint8_t *p = buffer + i * size;
+                       if (size > 12)
+                               dmi_write(target, DMI_SBDATA3,
+                                               ((uint32_t) p[12]) |
+                                               (((uint32_t) p[13]) << 8) |
+                                               (((uint32_t) p[14]) << 16) |
+                                               (((uint32_t) p[15]) << 24));
+                       if (size > 8)
+                               dmi_write(target, DMI_SBDATA2,
+                                               ((uint32_t) p[8]) |
+                                               (((uint32_t) p[9]) << 8) |
+                                               (((uint32_t) p[10]) << 16) |
+                                               (((uint32_t) p[11]) << 24));
+                       if (size > 4)
+                               dmi_write(target, DMI_SBDATA1,
+                                               ((uint32_t) p[4]) |
+                                               (((uint32_t) p[5]) << 8) |
+                                               (((uint32_t) p[6]) << 16) |
+                                               (((uint32_t) p[7]) << 24));
+                       uint32_t value = p[0];
+                       if (size > 2) {
+                               value |= ((uint32_t) p[2]) << 16;
+                               value |= ((uint32_t) p[3]) << 24;
+                       }
+                       if (size > 1)
+                               value |= ((uint32_t) p[1]) << 8;
+                       dmi_write(target, DMI_SBDATA0, value);
+
+                       log_memory_access(address + i * size, value, size, false);
+
+                       if (info->bus_master_write_delay) {
+                               jtag_add_runtest(info->bus_master_write_delay, TAP_IDLE);
+                               if (jtag_execute_queue() != ERROR_OK) {
+                                       LOG_ERROR("Failed to scan idle sequence");
+                                       return ERROR_FAIL;
+                               }
+                       }
+               }
+
+               if (read_sbcs_nonbusy(target, &sbcs) != ERROR_OK)
+                       return ERROR_FAIL;
+
+               if (get_field(sbcs, DMI_SBCS_SBBUSYERROR)) {
+                       /* We wrote while the target was busy. Slow down and try again. */
+                       dmi_write(target, DMI_SBCS, DMI_SBCS_SBBUSYERROR);
+                       next_address = sb_read_address(target);
+                       info->bus_master_write_delay += info->bus_master_write_delay / 10 + 1;
+                       continue;
+               }
+
+               unsigned error = get_field(sbcs, DMI_SBCS_SBERROR);
+               if (error == 0) {
+                       next_address = end_address;
+               } else {
+                       /* Some error indicating the bus access failed, but not because of
+                        * something we did wrong. */
+                       dmi_write(target, DMI_SBCS, DMI_SBCS_SBERROR);
+                       return ERROR_FAIL;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int write_memory_progbuf(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+       RISCV013_INFO(info);
+
+       LOG_DEBUG("writing %d words of %d bytes to 0x%08lx", count, size, (long)address);
+
+       select_dmi(target);
+
+       /* s0 holds the next address to write to
+        * s1 holds the next data value to write
+        */
+
+       int result = ERROR_OK;
+       uint64_t s0, s1;
+       if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+               return ERROR_FAIL;
+       if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* Write the program (store, increment) */
+       struct riscv_program program;
+       riscv_program_init(&program, target);
+
+       switch (size) {
+               case 1:
+                       riscv_program_sbr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+                       break;
+               case 2:
+                       riscv_program_shr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+                       break;
+               case 4:
+                       riscv_program_swr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
+                       break;
+               default:
+                       LOG_ERROR("Unsupported size: %d", size);
+                       result = ERROR_FAIL;
+                       goto error;
+       }
+
+       riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size);
+
+       result = riscv_program_ebreak(&program);
+       if (result != ERROR_OK)
+               goto error;
+       riscv_program_write(&program);
+
+       riscv_addr_t cur_addr = address;
+       riscv_addr_t fin_addr = address + (count * size);
+       bool setup_needed = true;
+       LOG_DEBUG("writing until final address 0x%016" PRIx64, fin_addr);
+       while (cur_addr < fin_addr) {
+               LOG_DEBUG("transferring burst starting at address 0x%016" PRIx64,
+                               cur_addr);
+
+               struct riscv_batch *batch = riscv_batch_alloc(
+                               target,
+                               32,
+                               info->dmi_busy_delay + info->ac_busy_delay);
+
+               /* To write another word, we put it in S1 and execute the program. */
+               unsigned start = (cur_addr - address) / size;
+               for (unsigned i = start; i < count; ++i) {
+                       unsigned offset = size*i;
+                       const uint8_t *t_buffer = buffer + offset;
+
+                       uint32_t value;
+                       switch (size) {
+                               case 1:
+                                       value = t_buffer[0];
+                                       break;
+                               case 2:
+                                       value = t_buffer[0]
+                                               | ((uint32_t) t_buffer[1] << 8);
+                                       break;
+                               case 4:
+                                       value = t_buffer[0]
+                                               | ((uint32_t) t_buffer[1] << 8)
+                                               | ((uint32_t) t_buffer[2] << 16)
+                                               | ((uint32_t) t_buffer[3] << 24);
+                                       break;
+                               default:
+                                       LOG_ERROR("unsupported access size: %d", size);
+                                       riscv_batch_free(batch);
+                                       result = ERROR_FAIL;
+                                       goto error;
+                       }
+
+                       log_memory_access(address + offset, value, size, false);
+                       cur_addr += size;
+
+                       if (setup_needed) {
+                               result = register_write_direct(target, GDB_REGNO_S0,
+                                               address + offset);
+                               if (result != ERROR_OK) {
+                                       riscv_batch_free(batch);
+                                       goto error;
+                               }
+
+                               /* Write value. */
+                               dmi_write(target, DMI_DATA0, value);
+
+                               /* Write and execute command that moves value into S1 and
+                                * executes program buffer. */
+                               uint32_t command = access_register_command(GDB_REGNO_S1, 32,
+                                               AC_ACCESS_REGISTER_POSTEXEC |
+                                               AC_ACCESS_REGISTER_TRANSFER |
+                                               AC_ACCESS_REGISTER_WRITE);
+                               result = execute_abstract_command(target, command);
+                               if (result != ERROR_OK) {
+                                       riscv_batch_free(batch);
+                                       goto error;
+                               }
+
+                               /* Turn on autoexec */
+                               dmi_write(target, DMI_ABSTRACTAUTO,
+                                               1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET);
+
+                               setup_needed = false;
+                       } else {
+                               riscv_batch_add_dmi_write(batch, DMI_DATA0, value);
+                               if (riscv_batch_full(batch))
+                                       break;
+                       }
+               }
+
+               result = riscv_batch_run(batch);
+               riscv_batch_free(batch);
+               if (result != ERROR_OK)
+                       goto error;
+
+               /* Note that if the scan resulted in a Busy DMI response, it
+                * is this read to abstractcs that will cause the dmi_busy_delay
+                * to be incremented if necessary. */
+
+               uint32_t abstractcs;
+               if (dmi_read(target, &abstractcs, DMI_ABSTRACTCS) != ERROR_OK)
+                       goto error;
+               while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY))
+                       if (dmi_read(target, &abstractcs, DMI_ABSTRACTCS) != ERROR_OK)
+                               return ERROR_FAIL;
+               info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR);
+               switch (info->cmderr) {
+                       case CMDERR_NONE:
+                               LOG_DEBUG("successful (partial?) memory write");
+                               break;
+                       case CMDERR_BUSY:
+                               LOG_DEBUG("memory write resulted in busy response");
+                               riscv013_clear_abstract_error(target);
+                               increase_ac_busy_delay(target);
+
+                               dmi_write(target, DMI_ABSTRACTAUTO, 0);
+                               result = register_read_direct(target, &cur_addr, GDB_REGNO_S0);
+                               if (result != ERROR_OK)
+                                       goto error;
+                               setup_needed = true;
+                               break;
+
+                       default:
+                               LOG_ERROR("error when writing memory, abstractcs=0x%08lx", (long)abstractcs);
+                               riscv013_clear_abstract_error(target);
+                               result = ERROR_FAIL;
+                               goto error;
+               }
+       }
+
+error:
+       dmi_write(target, DMI_ABSTRACTAUTO, 0);
+
+       if (register_write_direct(target, GDB_REGNO_S1, s1) != ERROR_OK)
+               return ERROR_FAIL;
+       if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+               return ERROR_FAIL;
+
+       if (execute_fence(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       return result;
+}
+
+static int write_memory(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+       RISCV013_INFO(info);
+       if (info->progbufsize >= 2 && !riscv_prefer_sba)
+               return write_memory_progbuf(target, address, size, count, buffer);
+
+       if ((get_field(info->sbcs, DMI_SBCS_SBACCESS8) && size == 1) ||
+                       (get_field(info->sbcs, DMI_SBCS_SBACCESS16) && size == 2) ||
+                       (get_field(info->sbcs, DMI_SBCS_SBACCESS32) && size == 4) ||
+                       (get_field(info->sbcs, DMI_SBCS_SBACCESS64) && size == 8) ||
+                       (get_field(info->sbcs, DMI_SBCS_SBACCESS128) && size == 16)) {
+               if (get_field(info->sbcs, DMI_SBCS_SBVERSION) == 0)
+                       return write_memory_bus_v0(target, address, size, count, buffer);
+               else if (get_field(info->sbcs, DMI_SBCS_SBVERSION) == 1)
+                       return write_memory_bus_v1(target, address, size, count, buffer);
+       }
+
+       if (info->progbufsize >= 2)
+               return write_memory_progbuf(target, address, size, count, buffer);
+
+       LOG_ERROR("Don't know how to write memory on this target.");
+       return ERROR_FAIL;
+}
+
+static int arch_state(struct target *target)
+{
+       return ERROR_OK;
+}
+
+struct target_type riscv013_target = {
+       .name = "riscv",
+
+       .init_target = init_target,
+       .deinit_target = deinit_target,
+       .examine = examine,
+
+       .poll = &riscv_openocd_poll,
+       .halt = &riscv_openocd_halt,
+       .resume = &riscv_openocd_resume,
+       .step = &riscv_openocd_step,
+
+       .assert_reset = assert_reset,
+       .deassert_reset = deassert_reset,
+
+       .read_memory = read_memory,
+       .write_memory = write_memory,
+
+       .arch_state = arch_state,
+};
+
+/*** 0.13-specific implementations of various RISC-V helper functions. ***/
+static int riscv013_get_register(struct target *target,
+               riscv_reg_t *value, int hid, int rid)
+{
+       LOG_DEBUG("reading register %s on hart %d", gdb_regno_name(rid), hid);
+
+       riscv_set_current_hartid(target, hid);
+
+       int result = ERROR_OK;
+       if (rid == GDB_REGNO_PC) {
+               result = register_read(target, value, GDB_REGNO_DPC);
+               LOG_DEBUG("read PC from DPC: 0x%016" PRIx64, *value);
+       } else if (rid == GDB_REGNO_PRIV) {
+               uint64_t dcsr;
+               result = register_read(target, &dcsr, GDB_REGNO_DCSR);
+               *value = get_field(dcsr, CSR_DCSR_PRV);
+       } else {
+               result = register_read(target, value, rid);
+               if (result != ERROR_OK)
+                       *value = -1;
+       }
+
+       return result;
+}
+
+static int riscv013_set_register(struct target *target, int hid, int rid, uint64_t value)
+{
+       LOG_DEBUG("writing 0x%" PRIx64 " to register %s on hart %d", value,
+                       gdb_regno_name(rid), hid);
+
+       riscv_set_current_hartid(target, hid);
+
+       if (rid <= GDB_REGNO_XPR31) {
+               return register_write_direct(target, rid, value);
+       } else if (rid == GDB_REGNO_PC) {
+               LOG_DEBUG("writing PC to DPC: 0x%016" PRIx64, value);
+               register_write_direct(target, GDB_REGNO_DPC, value);
+               uint64_t actual_value;
+               register_read_direct(target, &actual_value, GDB_REGNO_DPC);
+               LOG_DEBUG("  actual DPC written: 0x%016" PRIx64, actual_value);
+               if (value != actual_value) {
+                       LOG_ERROR("Written PC (0x%" PRIx64 ") does not match read back "
+                                       "value (0x%" PRIx64 ")", value, actual_value);
+                       return ERROR_FAIL;
+               }
+       } else if (rid == GDB_REGNO_PRIV) {
+               uint64_t dcsr;
+               register_read(target, &dcsr, GDB_REGNO_DCSR);
+               dcsr = set_field(dcsr, CSR_DCSR_PRV, value);
+               return register_write_direct(target, GDB_REGNO_DCSR, dcsr);
+       } else {
+               return register_write_direct(target, rid, value);
+       }
+
+       return ERROR_OK;
+}
+
+static int riscv013_select_current_hart(struct target *target)
+{
+       RISCV_INFO(r);
+
+       dm013_info_t *dm = get_dm(target);
+       if (r->current_hartid == dm->current_hartid)
+               return ERROR_OK;
+
+       uint32_t dmcontrol;
+       /* TODO: can't we just "dmcontrol = DMI_DMACTIVE"? */
+       if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
+               return ERROR_FAIL;
+       dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
+       int result = dmi_write(target, DMI_DMCONTROL, dmcontrol);
+       dm->current_hartid = r->current_hartid;
+       return result;
+}
+
+static int riscv013_halt_current_hart(struct target *target)
+{
+       RISCV_INFO(r);
+       LOG_DEBUG("halting hart %d", r->current_hartid);
+       if (riscv_is_halted(target))
+               LOG_ERROR("Hart %d is already halted!", r->current_hartid);
+
+       /* Issue the halt command, and then wait for the current hart to halt. */
+       uint32_t dmcontrol;
+       if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
+               return ERROR_FAIL;
+       dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 1);
+       dmi_write(target, DMI_DMCONTROL, dmcontrol);
+       for (size_t i = 0; i < 256; ++i)
+               if (riscv_is_halted(target))
+                       break;
+
+       if (!riscv_is_halted(target)) {
+               uint32_t dmstatus;
+               if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
+                       return ERROR_FAIL;
+               if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
+                       return ERROR_FAIL;
+
+               LOG_ERROR("unable to halt hart %d", r->current_hartid);
+               LOG_ERROR("  dmcontrol=0x%08x", dmcontrol);
+               LOG_ERROR("  dmstatus =0x%08x", dmstatus);
+               return ERROR_FAIL;
+       }
+
+       dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 0);
+       dmi_write(target, DMI_DMCONTROL, dmcontrol);
+
+       return ERROR_OK;
+}
+
+static int riscv013_resume_current_hart(struct target *target)
+{
+       return riscv013_step_or_resume_current_hart(target, false);
+}
+
+static int riscv013_step_current_hart(struct target *target)
+{
+       return riscv013_step_or_resume_current_hart(target, true);
+}
+
+static int riscv013_on_resume(struct target *target)
+{
+       return riscv013_on_step_or_resume(target, false);
+}
+
+static int riscv013_on_step(struct target *target)
+{
+       return riscv013_on_step_or_resume(target, true);
+}
+
+static int riscv013_on_halt(struct target *target)
+{
+       return ERROR_OK;
+}
+
+static bool riscv013_is_halted(struct target *target)
+{
+       uint32_t dmstatus;
+       if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
+               return false;
+       if (get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL))
+               LOG_ERROR("Hart %d is unavailable.", riscv_current_hartid(target));
+       if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT))
+               LOG_ERROR("Hart %d doesn't exist.", riscv_current_hartid(target));
+       if (get_field(dmstatus, DMI_DMSTATUS_ANYHAVERESET)) {
+               int hartid = riscv_current_hartid(target);
+               LOG_INFO("Hart %d unexpectedly reset!", hartid);
+               /* TODO: Can we make this more obvious to eg. a gdb user? */
+               uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE |
+                       DMI_DMCONTROL_ACKHAVERESET;
+               dmcontrol = set_hartsel(dmcontrol, hartid);
+               /* If we had been halted when we reset, request another halt. If we
+                * ended up running out of reset, then the user will (hopefully) get a
+                * message that a reset happened, that the target is running, and then
+                * that it is halted again once the request goes through.
+                */
+               if (target->state == TARGET_HALTED)
+                       dmcontrol |= DMI_DMCONTROL_HALTREQ;
+               dmi_write(target, DMI_DMCONTROL, dmcontrol);
+       }
+       return get_field(dmstatus, DMI_DMSTATUS_ALLHALTED);
+}
+
+static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
+{
+       riscv_reg_t dcsr;
+       int result = register_read(target, &dcsr, GDB_REGNO_DCSR);
+       if (result != ERROR_OK)
+               return RISCV_HALT_UNKNOWN;
+
+       switch (get_field(dcsr, CSR_DCSR_CAUSE)) {
+       case CSR_DCSR_CAUSE_SWBP:
+               return RISCV_HALT_BREAKPOINT;
+       case CSR_DCSR_CAUSE_TRIGGER:
+               /* We could get here before triggers are enumerated if a trigger was
+                * already set when we connected. Force enumeration now, which has the
+                * side effect of clearing any triggers we did not set. */
+               riscv_enumerate_triggers(target);
+               return RISCV_HALT_TRIGGER;
+       case CSR_DCSR_CAUSE_STEP:
+               return RISCV_HALT_SINGLESTEP;
+       case CSR_DCSR_CAUSE_DEBUGINT:
+       case CSR_DCSR_CAUSE_HALT:
+               return RISCV_HALT_INTERRUPT;
+       }
+
+       LOG_ERROR("Unknown DCSR cause field: %x", (int)get_field(dcsr, CSR_DCSR_CAUSE));
+       LOG_ERROR("  dcsr=0x%016lx", (long)dcsr);
+       return RISCV_HALT_UNKNOWN;
+}
+
+int riscv013_write_debug_buffer(struct target *target, unsigned index, riscv_insn_t data)
+{
+       return dmi_write(target, DMI_PROGBUF0 + index, data);
+}
+
+riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned index)
+{
+       uint32_t value;
+       dmi_read(target, &value, DMI_PROGBUF0 + index);
+       return value;
+}
+
+int riscv013_execute_debug_buffer(struct target *target)
+{
+       uint32_t run_program = 0;
+       run_program = set_field(run_program, AC_ACCESS_REGISTER_SIZE, 2);
+       run_program = set_field(run_program, AC_ACCESS_REGISTER_POSTEXEC, 1);
+       run_program = set_field(run_program, AC_ACCESS_REGISTER_TRANSFER, 0);
+       run_program = set_field(run_program, AC_ACCESS_REGISTER_REGNO, 0x1000);
+
+       return execute_abstract_command(target, run_program);
+}
+
+void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d)
+{
+       RISCV013_INFO(info);
+       buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_WRITE);
+       buf_set_u64((unsigned char *)buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, d);
+       buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a);
+}
+
+void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a)
+{
+       RISCV013_INFO(info);
+       buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_READ);
+       buf_set_u64((unsigned char *)buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, 0);
+       buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a);
+}
+
+void riscv013_fill_dmi_nop_u64(struct target *target, char *buf)
+{
+       RISCV013_INFO(info);
+       buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_NOP);
+       buf_set_u64((unsigned char *)buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, 0);
+       buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, 0);
+}
+
+int riscv013_dmi_write_u64_bits(struct target *target)
+{
+       RISCV013_INFO(info);
+       return info->abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH;
+}
+
+static int maybe_execute_fence_i(struct target *target)
+{
+       RISCV013_INFO(info);
+       RISCV_INFO(r);
+       if (info->progbufsize + r->impebreak >= 2) {
+               struct riscv_program program;
+               riscv_program_init(&program, target);
+               if (riscv_program_fence_i(&program) != ERROR_OK)
+                       return ERROR_FAIL;
+               if (riscv_program_exec(&program, target) != ERROR_OK) {
+                       LOG_ERROR("Failed to execute fence.i");
+                       return ERROR_FAIL;
+               }
+       }
+       return ERROR_OK;
+}
+
+/* Helper Functions. */
+static int riscv013_on_step_or_resume(struct target *target, bool step)
+{
+       if (maybe_execute_fence_i(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* We want to twiddle some bits in the debug CSR so debugging works. */
+       riscv_reg_t dcsr;
+       int result = register_read(target, &dcsr, GDB_REGNO_DCSR);
+       if (result != ERROR_OK)
+               return result;
+       dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
+       dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, 1);
+       dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, 1);
+       dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, 1);
+       return riscv_set_register(target, GDB_REGNO_DCSR, dcsr);
+}
+
+static int riscv013_step_or_resume_current_hart(struct target *target, bool step)
+{
+       RISCV_INFO(r);
+       LOG_DEBUG("resuming hart %d (for step?=%d)", r->current_hartid, step);
+       if (!riscv_is_halted(target)) {
+               LOG_ERROR("Hart %d is not halted!", r->current_hartid);
+               return ERROR_FAIL;
+       }
+
+       if (maybe_execute_fence_i(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* Issue the resume command, and then wait for the current hart to resume. */
+       uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE;
+       dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
+       dmi_write(target, DMI_DMCONTROL, dmcontrol | DMI_DMCONTROL_RESUMEREQ);
+
+       uint32_t dmstatus;
+       for (size_t i = 0; i < 256; ++i) {
+               usleep(10);
+               if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
+                       return ERROR_FAIL;
+               if (get_field(dmstatus, DMI_DMSTATUS_ALLRESUMEACK) == 0)
+                       continue;
+               if (step && get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0)
+                       continue;
+
+               dmi_write(target, DMI_DMCONTROL, dmcontrol);
+               return ERROR_OK;
+       }
+
+       LOG_ERROR("unable to resume hart %d", r->current_hartid);
+       if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
+               return ERROR_FAIL;
+       LOG_ERROR("  dmcontrol=0x%08x", dmcontrol);
+       if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
+               return ERROR_FAIL;
+       LOG_ERROR("  dmstatus =0x%08x", dmstatus);
+
+       if (step) {
+               LOG_ERROR("  was stepping, halting");
+               riscv013_halt_current_hart(target);
+               return ERROR_OK;
+       }
+
+       return ERROR_FAIL;
+}
+
+void riscv013_clear_abstract_error(struct target *target)
+{
+       /* Wait for busy to go away. */
+       time_t start = time(NULL);
+       uint32_t abstractcs;
+       dmi_read(target, &abstractcs, DMI_ABSTRACTCS);
+       while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY)) {
+               dmi_read(target, &abstractcs, DMI_ABSTRACTCS);
+
+               if (time(NULL) - start > riscv_command_timeout_sec) {
+                       LOG_ERROR("abstractcs.busy is not going low after %d seconds "
+                                       "(abstractcs=0x%x). The target is either really slow or "
+                                       "broken. You could increase the timeout with riscv "
+                                       "set_command_timeout_sec.",
+                                       riscv_command_timeout_sec, abstractcs);
+                       break;
+               }
+       }
+       /* Clear the error status. */
+       dmi_write(target, DMI_ABSTRACTCS, abstractcs & DMI_ABSTRACTCS_CMDERR);
+}
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
new file mode 100644 (file)
index 0000000..8d76c4a
--- /dev/null
@@ -0,0 +1,2512 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <time.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "target/target.h"
+#include "target/algorithm.h"
+#include "target/target_type.h"
+#include "log.h"
+#include "jtag/jtag.h"
+#include "target/register.h"
+#include "target/breakpoints.h"
+#include "helper/time_support.h"
+#include "riscv.h"
+#include "gdb_regs.h"
+#include "rtos/rtos.h"
+
+/**
+ * Since almost everything can be accomplish by scanning the dbus register, all
+ * functions here assume dbus is already selected. The exception are functions
+ * called directly by OpenOCD, which can't assume anything about what's
+ * currently in IR. They should set IR to dbus explicitly.
+ */
+
+/**
+ * Code structure
+ *
+ * At the bottom of the stack are the OpenOCD JTAG functions:
+ *             jtag_add_[id]r_scan
+ *             jtag_execute_query
+ *             jtag_add_runtest
+ *
+ * There are a few functions to just instantly shift a register and get its
+ * value:
+ *             dtmcontrol_scan
+ *             idcode_scan
+ *             dbus_scan
+ *
+ * Because doing one scan and waiting for the result is slow, most functions
+ * batch up a bunch of dbus writes and then execute them all at once. They use
+ * the scans "class" for this:
+ *             scans_new
+ *             scans_delete
+ *             scans_execute
+ *             scans_add_...
+ * Usually you new(), call a bunch of add functions, then execute() and look
+ * at the results by calling scans_get...()
+ *
+ * Optimized functions will directly use the scans class above, but slightly
+ * lazier code will use the cache functions that in turn use the scans
+ * functions:
+ *             cache_get...
+ *             cache_set...
+ *             cache_write
+ * cache_set... update a local structure, which is then synced to the target
+ * with cache_write(). Only Debug RAM words that are actually changed are sent
+ * to the target. Afterwards use cache_get... to read results.
+ */
+
+#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
+#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
+
+#define DIM(x)         (sizeof(x)/sizeof(*x))
+
+/* Constants for legacy SiFive hardware breakpoints. */
+#define CSR_BPCONTROL_X                        (1<<0)
+#define CSR_BPCONTROL_W                        (1<<1)
+#define CSR_BPCONTROL_R                        (1<<2)
+#define CSR_BPCONTROL_U                        (1<<3)
+#define CSR_BPCONTROL_S                        (1<<4)
+#define CSR_BPCONTROL_H                        (1<<5)
+#define CSR_BPCONTROL_M                        (1<<6)
+#define CSR_BPCONTROL_BPMATCH  (0xf<<7)
+#define CSR_BPCONTROL_BPACTION (0xff<<11)
+
+#define DEBUG_ROM_START         0x800
+#define DEBUG_ROM_RESUME       (DEBUG_ROM_START + 4)
+#define DEBUG_ROM_EXCEPTION    (DEBUG_ROM_START + 8)
+#define DEBUG_RAM_START         0x400
+
+#define SETHALTNOT                             0x10c
+
+/*** JTAG registers. ***/
+
+#define DTMCONTROL                                     0x10
+#define DTMCONTROL_DBUS_RESET          (1<<16)
+#define DTMCONTROL_IDLE                                (7<<10)
+#define DTMCONTROL_ADDRBITS                    (0xf<<4)
+#define DTMCONTROL_VERSION                     (0xf)
+
+#define DBUS                                           0x11
+#define DBUS_OP_START                          0
+#define DBUS_OP_SIZE                           2
+typedef enum {
+       DBUS_OP_NOP = 0,
+       DBUS_OP_READ = 1,
+       DBUS_OP_WRITE = 2
+} dbus_op_t;
+typedef enum {
+       DBUS_STATUS_SUCCESS = 0,
+       DBUS_STATUS_FAILED = 2,
+       DBUS_STATUS_BUSY = 3
+} dbus_status_t;
+#define DBUS_DATA_START                                2
+#define DBUS_DATA_SIZE                         34
+#define DBUS_ADDRESS_START                     36
+
+typedef enum {
+       RE_OK,
+       RE_FAIL,
+       RE_AGAIN
+} riscv_error_t;
+
+typedef enum slot {
+       SLOT0,
+       SLOT1,
+       SLOT_LAST,
+} slot_t;
+
+/*** Debug Bus registers. ***/
+
+#define DMCONTROL                              0x10
+#define DMCONTROL_INTERRUPT            (((uint64_t)1)<<33)
+#define DMCONTROL_HALTNOT              (((uint64_t)1)<<32)
+#define DMCONTROL_BUSERROR             (7<<19)
+#define DMCONTROL_SERIAL               (3<<16)
+#define DMCONTROL_AUTOINCREMENT        (1<<15)
+#define DMCONTROL_ACCESS               (7<<12)
+#define DMCONTROL_HARTID               (0x3ff<<2)
+#define DMCONTROL_NDRESET              (1<<1)
+#define DMCONTROL_FULLRESET            1
+
+#define DMINFO                                 0x11
+#define DMINFO_ABUSSIZE                        (0x7fU<<25)
+#define DMINFO_SERIALCOUNT             (0xf<<21)
+#define DMINFO_ACCESS128               (1<<20)
+#define DMINFO_ACCESS64                        (1<<19)
+#define DMINFO_ACCESS32                        (1<<18)
+#define DMINFO_ACCESS16                        (1<<17)
+#define DMINFO_ACCESS8                 (1<<16)
+#define DMINFO_DRAMSIZE                        (0x3f<<10)
+#define DMINFO_AUTHENTICATED   (1<<5)
+#define DMINFO_AUTHBUSY                        (1<<4)
+#define DMINFO_AUTHTYPE                        (3<<2)
+#define DMINFO_VERSION                 3
+
+/*** Info about the core being debugged. ***/
+
+#define DBUS_ADDRESS_UNKNOWN   0xffff
+
+#define MAX_HWBPS                      16
+#define DRAM_CACHE_SIZE                16
+
+uint8_t ir_dtmcontrol[1] = {DTMCONTROL};
+struct scan_field select_dtmcontrol = {
+       .in_value = NULL,
+       .out_value = ir_dtmcontrol
+};
+uint8_t ir_dbus[1] = {DBUS};
+struct scan_field select_dbus = {
+       .in_value = NULL,
+       .out_value = ir_dbus
+};
+uint8_t ir_idcode[1] = {0x1};
+struct scan_field select_idcode = {
+       .in_value = NULL,
+       .out_value = ir_idcode
+};
+
+struct trigger {
+       uint64_t address;
+       uint32_t length;
+       uint64_t mask;
+       uint64_t value;
+       bool read, write, execute;
+       int unique_id;
+};
+
+/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/
+int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC;
+
+/* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
+int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC;
+
+bool riscv_prefer_sba;
+
+/* In addition to the ones in the standard spec, we'll also expose additional
+ * CSRs in this list.
+ * The list is either NULL, or a series of ranges (inclusive), terminated with
+ * 1,0. */
+struct {
+       uint16_t low, high;
+} *expose_csr;
+
+static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
+{
+       struct scan_field field;
+       uint8_t in_value[4];
+       uint8_t out_value[4];
+
+       buf_set_u32(out_value, 0, 32, out);
+
+       jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE);
+
+       field.num_bits = 32;
+       field.out_value = out_value;
+       field.in_value = in_value;
+       jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+       /* Always return to dbus. */
+       jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
+
+       int retval = jtag_execute_queue();
+       if (retval != ERROR_OK) {
+               LOG_ERROR("failed jtag scan: %d", retval);
+               return retval;
+       }
+
+       uint32_t in = buf_get_u32(field.in_value, 0, 32);
+       LOG_DEBUG("DTMCONTROL: 0x%x -> 0x%x", out, in);
+
+       return in;
+}
+
+static struct target_type *get_target_type(struct target *target)
+{
+       riscv_info_t *info = (riscv_info_t *) target->arch_info;
+
+       if (!info) {
+               LOG_ERROR("Target has not been initialized");
+               return NULL;
+       }
+
+       switch (info->dtm_version) {
+               case 0:
+                       return &riscv011_target;
+               case 1:
+                       return &riscv013_target;
+               default:
+                       LOG_ERROR("Unsupported DTM version: %d", info->dtm_version);
+                       return NULL;
+       }
+}
+
+static int riscv_init_target(struct command_context *cmd_ctx,
+               struct target *target)
+{
+       LOG_DEBUG("riscv_init_target()");
+       target->arch_info = calloc(1, sizeof(riscv_info_t));
+       if (!target->arch_info)
+               return ERROR_FAIL;
+       riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       riscv_info_init(target, info);
+       info->cmd_ctx = cmd_ctx;
+
+       select_dtmcontrol.num_bits = target->tap->ir_length;
+       select_dbus.num_bits = target->tap->ir_length;
+       select_idcode.num_bits = target->tap->ir_length;
+
+       riscv_semihosting_init(target);
+
+       return ERROR_OK;
+}
+
+static void riscv_deinit_target(struct target *target)
+{
+       LOG_DEBUG("riscv_deinit_target()");
+       struct target_type *tt = get_target_type(target);
+       if (tt) {
+               tt->deinit_target(target);
+               riscv_info_t *info = (riscv_info_t *) target->arch_info;
+               free(info);
+       }
+       target->arch_info = NULL;
+}
+
+static int oldriscv_halt(struct target *target)
+{
+       struct target_type *tt = get_target_type(target);
+       return tt->halt(target);
+}
+
+static void trigger_from_breakpoint(struct trigger *trigger,
+               const struct breakpoint *breakpoint)
+{
+       trigger->address = breakpoint->address;
+       trigger->length = breakpoint->length;
+       trigger->mask = ~0LL;
+       trigger->read = false;
+       trigger->write = false;
+       trigger->execute = true;
+       /* unique_id is unique across both breakpoints and watchpoints. */
+       trigger->unique_id = breakpoint->unique_id;
+}
+
+static int maybe_add_trigger_t1(struct target *target, unsigned hartid,
+               struct trigger *trigger, uint64_t tdata1)
+{
+       RISCV_INFO(r);
+
+       const uint32_t bpcontrol_x = 1<<0;
+       const uint32_t bpcontrol_w = 1<<1;
+       const uint32_t bpcontrol_r = 1<<2;
+       const uint32_t bpcontrol_u = 1<<3;
+       const uint32_t bpcontrol_s = 1<<4;
+       const uint32_t bpcontrol_h = 1<<5;
+       const uint32_t bpcontrol_m = 1<<6;
+       const uint32_t bpcontrol_bpmatch = 0xf << 7;
+       const uint32_t bpcontrol_bpaction = 0xff << 11;
+
+       if (tdata1 & (bpcontrol_r | bpcontrol_w | bpcontrol_x)) {
+               /* Trigger is already in use, presumably by user code. */
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       tdata1 = set_field(tdata1, bpcontrol_r, trigger->read);
+       tdata1 = set_field(tdata1, bpcontrol_w, trigger->write);
+       tdata1 = set_field(tdata1, bpcontrol_x, trigger->execute);
+       tdata1 = set_field(tdata1, bpcontrol_u,
+                       !!(r->misa[hartid] & (1 << ('U' - 'A'))));
+       tdata1 = set_field(tdata1, bpcontrol_s,
+                       !!(r->misa[hartid] & (1 << ('S' - 'A'))));
+       tdata1 = set_field(tdata1, bpcontrol_h,
+                       !!(r->misa[hartid] & (1 << ('H' - 'A'))));
+       tdata1 |= bpcontrol_m;
+       tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */
+       tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */
+
+       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1);
+
+       riscv_reg_t tdata1_rb;
+       if (riscv_get_register_on_hart(target, &tdata1_rb, hartid,
+                               GDB_REGNO_TDATA1) != ERROR_OK)
+               return ERROR_FAIL;
+       LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
+
+       if (tdata1 != tdata1_rb) {
+               LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
+                               PRIx64 " to tdata1 it contains 0x%" PRIx64,
+                               tdata1, tdata1_rb);
+               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address);
+
+       return ERROR_OK;
+}
+
+static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
+               struct trigger *trigger, uint64_t tdata1)
+{
+       RISCV_INFO(r);
+
+       /* tselect is already set */
+       if (tdata1 & (MCONTROL_EXECUTE | MCONTROL_STORE | MCONTROL_LOAD)) {
+               /* Trigger is already in use, presumably by user code. */
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       /* address/data match trigger */
+       tdata1 |= MCONTROL_DMODE(riscv_xlen(target));
+       tdata1 = set_field(tdata1, MCONTROL_ACTION,
+                       MCONTROL_ACTION_DEBUG_MODE);
+       tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
+       tdata1 |= MCONTROL_M;
+       if (r->misa[hartid] & (1 << ('H' - 'A')))
+               tdata1 |= MCONTROL_H;
+       if (r->misa[hartid] & (1 << ('S' - 'A')))
+               tdata1 |= MCONTROL_S;
+       if (r->misa[hartid] & (1 << ('U' - 'A')))
+               tdata1 |= MCONTROL_U;
+
+       if (trigger->execute)
+               tdata1 |= MCONTROL_EXECUTE;
+       if (trigger->read)
+               tdata1 |= MCONTROL_LOAD;
+       if (trigger->write)
+               tdata1 |= MCONTROL_STORE;
+
+       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1);
+
+       uint64_t tdata1_rb;
+       int result = riscv_get_register_on_hart(target, &tdata1_rb, hartid, GDB_REGNO_TDATA1);
+       if (result != ERROR_OK)
+               return result;
+       LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
+
+       if (tdata1 != tdata1_rb) {
+               LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
+                               PRIx64 " to tdata1 it contains 0x%" PRIx64,
+                               tdata1, tdata1_rb);
+               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address);
+
+       return ERROR_OK;
+}
+
+static int add_trigger(struct target *target, struct trigger *trigger)
+{
+       RISCV_INFO(r);
+
+       if (riscv_enumerate_triggers(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* In RTOS mode, we need to set the same trigger in the same slot on every
+        * hart, to keep up the illusion that each hart is a thread running on the
+        * same core. */
+
+       /* Otherwise, we just set the trigger on the one hart this target deals
+        * with. */
+
+       riscv_reg_t tselect[RISCV_MAX_HARTS];
+
+       int first_hart = -1;
+       for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
+               if (!riscv_hart_enabled(target, hartid))
+                       continue;
+               if (first_hart < 0)
+                       first_hart = hartid;
+               int result = riscv_get_register_on_hart(target, &tselect[hartid],
+                               hartid, GDB_REGNO_TSELECT);
+               if (result != ERROR_OK)
+                       return result;
+       }
+       assert(first_hart >= 0);
+
+       unsigned int i;
+       for (i = 0; i < r->trigger_count[first_hart]; i++) {
+               if (r->trigger_unique_id[i] != -1)
+                       continue;
+
+               riscv_set_register_on_hart(target, first_hart, GDB_REGNO_TSELECT, i);
+
+               uint64_t tdata1;
+               int result = riscv_get_register_on_hart(target, &tdata1, first_hart,
+                               GDB_REGNO_TDATA1);
+               if (result != ERROR_OK)
+                       return result;
+               int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
+
+               result = ERROR_OK;
+               for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
+                       if (!riscv_hart_enabled(target, hartid))
+                               continue;
+                       if (hartid > first_hart)
+                               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i);
+                       switch (type) {
+                               case 1:
+                                       result = maybe_add_trigger_t1(target, hartid, trigger, tdata1);
+                                       break;
+                               case 2:
+                                       result = maybe_add_trigger_t2(target, hartid, trigger, tdata1);
+                                       break;
+                               default:
+                                       LOG_DEBUG("trigger %d has unknown type %d", i, type);
+                                       continue;
+                       }
+
+                       if (result != ERROR_OK)
+                               continue;
+               }
+
+               if (result != ERROR_OK)
+                       continue;
+
+               LOG_DEBUG("Using trigger %d (type %d) for bp %d", i, type,
+                               trigger->unique_id);
+               r->trigger_unique_id[i] = trigger->unique_id;
+               break;
+       }
+
+       for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
+               if (!riscv_hart_enabled(target, hartid))
+                       continue;
+               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT,
+                               tselect[hartid]);
+       }
+
+       if (i >= r->trigger_count[first_hart]) {
+               LOG_ERROR("Couldn't find an available hardware trigger.");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       return ERROR_OK;
+}
+
+int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
+{
+       if (breakpoint->type == BKPT_SOFT) {
+               if (target_read_memory(target, breakpoint->address, breakpoint->length, 1,
+                                       breakpoint->orig_instr) != ERROR_OK) {
+                       LOG_ERROR("Failed to read original instruction at 0x%" TARGET_PRIxADDR,
+                                       breakpoint->address);
+                       return ERROR_FAIL;
+               }
+
+               int retval;
+               if (breakpoint->length == 4)
+                       retval = target_write_u32(target, breakpoint->address, ebreak());
+               else
+                       retval = target_write_u16(target, breakpoint->address, ebreak_c());
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%"
+                                       TARGET_PRIxADDR, breakpoint->length, breakpoint->address);
+                       return ERROR_FAIL;
+               }
+
+       } else if (breakpoint->type == BKPT_HARD) {
+               struct trigger trigger;
+               trigger_from_breakpoint(&trigger, breakpoint);
+               int result = add_trigger(target, &trigger);
+               if (result != ERROR_OK)
+                       return result;
+
+       } else {
+               LOG_INFO("OpenOCD only supports hardware and software breakpoints.");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       breakpoint->set = true;
+
+       return ERROR_OK;
+}
+
+static int remove_trigger(struct target *target, struct trigger *trigger)
+{
+       RISCV_INFO(r);
+
+       if (riscv_enumerate_triggers(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       int first_hart = -1;
+       for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
+               if (!riscv_hart_enabled(target, hartid))
+                       continue;
+               if (first_hart < 0) {
+                       first_hart = hartid;
+                       break;
+               }
+       }
+       assert(first_hart >= 0);
+
+       unsigned int i;
+       for (i = 0; i < r->trigger_count[first_hart]; i++) {
+               if (r->trigger_unique_id[i] == trigger->unique_id)
+                       break;
+       }
+       if (i >= r->trigger_count[first_hart]) {
+               LOG_ERROR("Couldn't find the hardware resources used by hardware "
+                               "trigger.");
+               return ERROR_FAIL;
+       }
+       LOG_DEBUG("Stop using resource %d for bp %d", i, trigger->unique_id);
+       for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
+               if (!riscv_hart_enabled(target, hartid))
+                       continue;
+               riscv_reg_t tselect;
+               int result = riscv_get_register_on_hart(target, &tselect, hartid, GDB_REGNO_TSELECT);
+               if (result != ERROR_OK)
+                       return result;
+               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i);
+               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
+               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect);
+       }
+       r->trigger_unique_id[i] = -1;
+
+       return ERROR_OK;
+}
+
+int riscv_remove_breakpoint(struct target *target,
+               struct breakpoint *breakpoint)
+{
+       if (breakpoint->type == BKPT_SOFT) {
+               if (target_write_memory(target, breakpoint->address, breakpoint->length, 1,
+                                       breakpoint->orig_instr) != ERROR_OK) {
+                       LOG_ERROR("Failed to restore instruction for %d-byte breakpoint at "
+                                       "0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address);
+                       return ERROR_FAIL;
+               }
+
+       } else if (breakpoint->type == BKPT_HARD) {
+               struct trigger trigger;
+               trigger_from_breakpoint(&trigger, breakpoint);
+               int result = remove_trigger(target, &trigger);
+               if (result != ERROR_OK)
+                       return result;
+
+       } else {
+               LOG_INFO("OpenOCD only supports hardware and software breakpoints.");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       breakpoint->set = false;
+
+       return ERROR_OK;
+}
+
+static void trigger_from_watchpoint(struct trigger *trigger,
+               const struct watchpoint *watchpoint)
+{
+       trigger->address = watchpoint->address;
+       trigger->length = watchpoint->length;
+       trigger->mask = watchpoint->mask;
+       trigger->value = watchpoint->value;
+       trigger->read = (watchpoint->rw == WPT_READ || watchpoint->rw == WPT_ACCESS);
+       trigger->write = (watchpoint->rw == WPT_WRITE || watchpoint->rw == WPT_ACCESS);
+       trigger->execute = false;
+       /* unique_id is unique across both breakpoints and watchpoints. */
+       trigger->unique_id = watchpoint->unique_id;
+}
+
+int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint)
+{
+       struct trigger trigger;
+       trigger_from_watchpoint(&trigger, watchpoint);
+
+       int result = add_trigger(target, &trigger);
+       if (result != ERROR_OK)
+               return result;
+       watchpoint->set = true;
+
+       return ERROR_OK;
+}
+
+int riscv_remove_watchpoint(struct target *target,
+               struct watchpoint *watchpoint)
+{
+       struct trigger trigger;
+       trigger_from_watchpoint(&trigger, watchpoint);
+
+       int result = remove_trigger(target, &trigger);
+       if (result != ERROR_OK)
+               return result;
+       watchpoint->set = false;
+
+       return ERROR_OK;
+}
+
+static int oldriscv_step(struct target *target, int current, uint32_t address,
+               int handle_breakpoints)
+{
+       struct target_type *tt = get_target_type(target);
+       return tt->step(target, current, address, handle_breakpoints);
+}
+
+static int old_or_new_riscv_step(
+               struct target *target,
+               int current,
+               target_addr_t address,
+               int handle_breakpoints
+){
+       RISCV_INFO(r);
+       LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
+       if (r->is_halted == NULL)
+               return oldriscv_step(target, current, address, handle_breakpoints);
+       else
+               return riscv_openocd_step(target, current, address, handle_breakpoints);
+}
+
+
+static int riscv_examine(struct target *target)
+{
+       LOG_DEBUG("riscv_examine()");
+       if (target_was_examined(target)) {
+               LOG_DEBUG("Target was already examined.");
+               return ERROR_OK;
+       }
+
+       /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
+
+       riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
+       LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
+       info->dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION);
+       LOG_DEBUG("  version=0x%x", info->dtm_version);
+
+       struct target_type *tt = get_target_type(target);
+       if (tt == NULL)
+               return ERROR_FAIL;
+
+       int result = tt->init_target(info->cmd_ctx, target);
+       if (result != ERROR_OK)
+               return result;
+
+       return tt->examine(target);
+}
+
+static int oldriscv_poll(struct target *target)
+{
+       struct target_type *tt = get_target_type(target);
+       return tt->poll(target);
+}
+
+static int old_or_new_riscv_poll(struct target *target)
+{
+       RISCV_INFO(r);
+       if (r->is_halted == NULL)
+               return oldriscv_poll(target);
+       else
+               return riscv_openocd_poll(target);
+}
+
+static int old_or_new_riscv_halt(struct target *target)
+{
+       RISCV_INFO(r);
+       if (r->is_halted == NULL)
+               return oldriscv_halt(target);
+       else
+               return riscv_openocd_halt(target);
+}
+
+static int riscv_assert_reset(struct target *target)
+{
+       struct target_type *tt = get_target_type(target);
+       return tt->assert_reset(target);
+}
+
+static int riscv_deassert_reset(struct target *target)
+{
+       LOG_DEBUG("RISCV DEASSERT RESET");
+       struct target_type *tt = get_target_type(target);
+       return tt->deassert_reset(target);
+}
+
+
+static int oldriscv_resume(struct target *target, int current, uint32_t address,
+               int handle_breakpoints, int debug_execution)
+{
+       struct target_type *tt = get_target_type(target);
+       return tt->resume(target, current, address, handle_breakpoints,
+                       debug_execution);
+}
+
+static int old_or_new_riscv_resume(
+               struct target *target,
+               int current,
+               target_addr_t address,
+               int handle_breakpoints,
+               int debug_execution
+){
+       RISCV_INFO(r);
+       LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
+       if (r->is_halted == NULL)
+               return oldriscv_resume(target, current, address, handle_breakpoints, debug_execution);
+       else
+               return riscv_openocd_resume(target, current, address, handle_breakpoints, debug_execution);
+}
+
+static int riscv_select_current_hart(struct target *target)
+{
+       RISCV_INFO(r);
+       if (r->rtos_hartid != -1 && riscv_rtos_enabled(target))
+               return riscv_set_current_hartid(target, r->rtos_hartid);
+       else
+               return riscv_set_current_hartid(target, target->coreid);
+}
+
+static int riscv_read_memory(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, uint8_t *buffer)
+{
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+       struct target_type *tt = get_target_type(target);
+       return tt->read_memory(target, address, size, count, buffer);
+}
+
+static int riscv_write_memory(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+       struct target_type *tt = get_target_type(target);
+       return tt->write_memory(target, address, size, count, buffer);
+}
+
+static int riscv_get_gdb_reg_list(struct target *target,
+               struct reg **reg_list[], int *reg_list_size,
+               enum target_register_class reg_class)
+{
+       RISCV_INFO(r);
+       LOG_DEBUG("reg_class=%d", reg_class);
+       LOG_DEBUG("rtos_hartid=%d current_hartid=%d", r->rtos_hartid, r->current_hartid);
+
+       if (!target->reg_cache) {
+               LOG_ERROR("Target not initialized. Return ERROR_FAIL.");
+               return ERROR_FAIL;
+       }
+
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       switch (reg_class) {
+               case REG_CLASS_GENERAL:
+                       *reg_list_size = 32;
+                       break;
+               case REG_CLASS_ALL:
+                       *reg_list_size = GDB_REGNO_COUNT;
+                       break;
+               default:
+                       LOG_ERROR("Unsupported reg_class: %d", reg_class);
+                       return ERROR_FAIL;
+       }
+
+       *reg_list = calloc(*reg_list_size, sizeof(struct reg *));
+       if (!*reg_list)
+               return ERROR_FAIL;
+
+       for (int i = 0; i < *reg_list_size; i++) {
+               assert(!target->reg_cache->reg_list[i].valid ||
+                               target->reg_cache->reg_list[i].size > 0);
+               (*reg_list)[i] = &target->reg_cache->reg_list[i];
+       }
+
+       return ERROR_OK;
+}
+
+static int riscv_arch_state(struct target *target)
+{
+       struct target_type *tt = get_target_type(target);
+       return tt->arch_state(target);
+}
+
+/* Algorithm must end with a software breakpoint instruction. */
+static int riscv_run_algorithm(struct target *target, int num_mem_params,
+               struct mem_param *mem_params, int num_reg_params,
+               struct reg_param *reg_params, target_addr_t entry_point,
+               target_addr_t exit_point, int timeout_ms, void *arch_info)
+{
+       riscv_info_t *info = (riscv_info_t *) target->arch_info;
+
+       if (num_mem_params > 0) {
+               LOG_ERROR("Memory parameters are not supported for RISC-V algorithms.");
+               return ERROR_FAIL;
+       }
+
+       if (target->state != TARGET_HALTED) {
+               LOG_WARNING("target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* Save registers */
+       struct reg *reg_pc = register_get_by_name(target->reg_cache, "pc", 1);
+       if (!reg_pc || reg_pc->type->get(reg_pc) != ERROR_OK)
+               return ERROR_FAIL;
+       uint64_t saved_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size);
+
+       uint64_t saved_regs[32];
+       for (int i = 0; i < num_reg_params; i++) {
+               LOG_DEBUG("save %s", reg_params[i].reg_name);
+               struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
+               if (!r) {
+                       LOG_ERROR("Couldn't find register named '%s'", reg_params[i].reg_name);
+                       return ERROR_FAIL;
+               }
+
+               if (r->size != reg_params[i].size) {
+                       LOG_ERROR("Register %s is %d bits instead of %d bits.",
+                                       reg_params[i].reg_name, r->size, reg_params[i].size);
+                       return ERROR_FAIL;
+               }
+
+               if (r->number > GDB_REGNO_XPR31) {
+                       LOG_ERROR("Only GPRs can be use as argument registers.");
+                       return ERROR_FAIL;
+               }
+
+               if (r->type->get(r) != ERROR_OK)
+                       return ERROR_FAIL;
+               saved_regs[r->number] = buf_get_u64(r->value, 0, r->size);
+               if (r->type->set(r, reg_params[i].value) != ERROR_OK)
+                       return ERROR_FAIL;
+       }
+
+
+       /* Disable Interrupts before attempting to run the algorithm. */
+       uint64_t current_mstatus;
+       uint8_t mstatus_bytes[8];
+
+       LOG_DEBUG("Disabling Interrupts");
+       struct reg *reg_mstatus = register_get_by_name(target->reg_cache,
+                       "mstatus", 1);
+       if (!reg_mstatus) {
+               LOG_ERROR("Couldn't find mstatus!");
+               return ERROR_FAIL;
+       }
+
+       reg_mstatus->type->get(reg_mstatus);
+       current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size);
+       uint64_t ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE;
+       buf_set_u64(mstatus_bytes, 0, info->xlen[0], set_field(current_mstatus,
+                               ie_mask, 0));
+
+       reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
+
+       /* Run algorithm */
+       LOG_DEBUG("resume at 0x%" TARGET_PRIxADDR, entry_point);
+       if (oldriscv_resume(target, 0, entry_point, 0, 0) != ERROR_OK)
+               return ERROR_FAIL;
+
+       int64_t start = timeval_ms();
+       while (target->state != TARGET_HALTED) {
+               LOG_DEBUG("poll()");
+               int64_t now = timeval_ms();
+               if (now - start > timeout_ms) {
+                       LOG_ERROR("Algorithm timed out after %d ms.", timeout_ms);
+                       LOG_ERROR("  now   = 0x%08x", (uint32_t) now);
+                       LOG_ERROR("  start = 0x%08x", (uint32_t) start);
+                       oldriscv_halt(target);
+                       old_or_new_riscv_poll(target);
+                       return ERROR_TARGET_TIMEOUT;
+               }
+
+               int result = old_or_new_riscv_poll(target);
+               if (result != ERROR_OK)
+                       return result;
+       }
+
+       if (reg_pc->type->get(reg_pc) != ERROR_OK)
+               return ERROR_FAIL;
+       uint64_t final_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size);
+       if (final_pc != exit_point) {
+               LOG_ERROR("PC ended up at 0x%" PRIx64 " instead of 0x%"
+                               TARGET_PRIxADDR, final_pc, exit_point);
+               return ERROR_FAIL;
+       }
+
+       /* Restore Interrupts */
+       LOG_DEBUG("Restoring Interrupts");
+       buf_set_u64(mstatus_bytes, 0, info->xlen[0], current_mstatus);
+       reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
+
+       /* Restore registers */
+       uint8_t buf[8];
+       buf_set_u64(buf, 0, info->xlen[0], saved_pc);
+       if (reg_pc->type->set(reg_pc, buf) != ERROR_OK)
+               return ERROR_FAIL;
+
+       for (int i = 0; i < num_reg_params; i++) {
+               LOG_DEBUG("restore %s", reg_params[i].reg_name);
+               struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
+               buf_set_u64(buf, 0, info->xlen[0], saved_regs[r->number]);
+               if (r->type->set(r, buf) != ERROR_OK)
+                       return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+/* Should run code on the target to perform CRC of
+memory. Not yet implemented.
+*/
+
+static int riscv_checksum_memory(struct target *target,
+               target_addr_t address, uint32_t count,
+               uint32_t *checksum)
+{
+       *checksum = 0xFFFFFFFF;
+       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+}
+
+/*** OpenOCD Helper Functions ***/
+
+enum riscv_poll_hart {
+       RPH_NO_CHANGE,
+       RPH_DISCOVERED_HALTED,
+       RPH_DISCOVERED_RUNNING,
+       RPH_ERROR
+};
+static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid)
+{
+       RISCV_INFO(r);
+       if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+               return RPH_ERROR;
+
+       LOG_DEBUG("polling hart %d, target->state=%d", hartid, target->state);
+
+       /* If OpenOCD thinks we're running but this hart is halted then it's time
+        * to raise an event. */
+       bool halted = riscv_is_halted(target);
+       if (target->state != TARGET_HALTED && halted) {
+               LOG_DEBUG("  triggered a halt");
+               r->on_halt(target);
+               return RPH_DISCOVERED_HALTED;
+       } else if (target->state != TARGET_RUNNING && !halted) {
+               LOG_DEBUG("  triggered running");
+               target->state = TARGET_RUNNING;
+               return RPH_DISCOVERED_RUNNING;
+       }
+
+       return RPH_NO_CHANGE;
+}
+
+/*** OpenOCD Interface ***/
+int riscv_openocd_poll(struct target *target)
+{
+       LOG_DEBUG("polling all harts");
+       int halted_hart = -1;
+       if (riscv_rtos_enabled(target)) {
+               /* Check every hart for an event. */
+               for (int i = 0; i < riscv_count_harts(target); ++i) {
+                       enum riscv_poll_hart out = riscv_poll_hart(target, i);
+                       switch (out) {
+                       case RPH_NO_CHANGE:
+                       case RPH_DISCOVERED_RUNNING:
+                               continue;
+                       case RPH_DISCOVERED_HALTED:
+                               halted_hart = i;
+                               break;
+                       case RPH_ERROR:
+                               return ERROR_FAIL;
+                       }
+               }
+               if (halted_hart == -1) {
+                       LOG_DEBUG("  no harts just halted, target->state=%d", target->state);
+                       return ERROR_OK;
+               }
+               LOG_DEBUG("  hart %d halted", halted_hart);
+
+               /* If we're here then at least one hart triggered.  That means
+                * we want to go and halt _every_ hart in the system, as that's
+                * the invariant we hold here.  Some harts might have already
+                * halted (as we're either in single-step mode or they also
+                * triggered a breakpoint), so don't attempt to halt those
+                * harts. */
+               for (int i = 0; i < riscv_count_harts(target); ++i)
+                       riscv_halt_one_hart(target, i);
+       } else {
+               enum riscv_poll_hart out = riscv_poll_hart(target,
+                               riscv_current_hartid(target));
+               if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING)
+                       return ERROR_OK;
+               else if (out == RPH_ERROR)
+                       return ERROR_FAIL;
+
+               halted_hart = riscv_current_hartid(target);
+               LOG_DEBUG("  hart %d halted", halted_hart);
+       }
+
+       target->state = TARGET_HALTED;
+       switch (riscv_halt_reason(target, halted_hart)) {
+       case RISCV_HALT_BREAKPOINT:
+               target->debug_reason = DBG_REASON_BREAKPOINT;
+               break;
+       case RISCV_HALT_TRIGGER:
+               target->debug_reason = DBG_REASON_WATCHPOINT;
+               break;
+       case RISCV_HALT_INTERRUPT:
+               target->debug_reason = DBG_REASON_DBGRQ;
+               break;
+       case RISCV_HALT_SINGLESTEP:
+               target->debug_reason = DBG_REASON_SINGLESTEP;
+               break;
+       case RISCV_HALT_UNKNOWN:
+               target->debug_reason = DBG_REASON_UNDEFINED;
+               break;
+       case RISCV_HALT_ERROR:
+               return ERROR_FAIL;
+       }
+
+       if (riscv_rtos_enabled(target)) {
+               target->rtos->current_threadid = halted_hart + 1;
+               target->rtos->current_thread = halted_hart + 1;
+       }
+
+       target->state = TARGET_HALTED;
+
+       if (target->debug_reason == DBG_REASON_BREAKPOINT) {
+               int retval;
+               if (riscv_semihosting(target, &retval) != 0)
+                       return retval;
+       }
+
+       target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+       return ERROR_OK;
+}
+
+int riscv_openocd_halt(struct target *target)
+{
+       RISCV_INFO(r);
+
+       LOG_DEBUG("halting all harts");
+
+       int out = riscv_halt_all_harts(target);
+       if (out != ERROR_OK) {
+               LOG_ERROR("Unable to halt all harts");
+               return out;
+       }
+
+       register_cache_invalidate(target->reg_cache);
+       if (riscv_rtos_enabled(target)) {
+               target->rtos->current_threadid = r->rtos_hartid + 1;
+               target->rtos->current_thread = r->rtos_hartid + 1;
+       }
+
+       target->state = TARGET_HALTED;
+       target->debug_reason = DBG_REASON_DBGRQ;
+       target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+       return out;
+}
+
+int riscv_openocd_resume(
+               struct target *target,
+               int current,
+               target_addr_t address,
+               int handle_breakpoints,
+               int debug_execution)
+{
+       LOG_DEBUG("debug_reason=%d", target->debug_reason);
+
+       if (!current)
+               riscv_set_register(target, GDB_REGNO_PC, address);
+
+       if (target->debug_reason == DBG_REASON_WATCHPOINT) {
+               /* To be able to run off a trigger, disable all the triggers, step, and
+                * then resume as usual. */
+               struct watchpoint *watchpoint = target->watchpoints;
+               bool trigger_temporarily_cleared[RISCV_MAX_HWBPS] = {0};
+
+               int i = 0;
+               int result = ERROR_OK;
+               while (watchpoint && result == ERROR_OK) {
+                       LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->set);
+                       trigger_temporarily_cleared[i] = watchpoint->set;
+                       if (watchpoint->set)
+                               result = riscv_remove_watchpoint(target, watchpoint);
+                       watchpoint = watchpoint->next;
+                       i++;
+               }
+
+               if (result == ERROR_OK)
+                       result = riscv_step_rtos_hart(target);
+
+               watchpoint = target->watchpoints;
+               i = 0;
+               while (watchpoint) {
+                       LOG_DEBUG("watchpoint %d: cleared=%d", i, trigger_temporarily_cleared[i]);
+                       if (trigger_temporarily_cleared[i]) {
+                               if (result == ERROR_OK)
+                                       result = riscv_add_watchpoint(target, watchpoint);
+                               else
+                                       riscv_add_watchpoint(target, watchpoint);
+                       }
+                       watchpoint = watchpoint->next;
+                       i++;
+               }
+
+               if (result != ERROR_OK)
+                       return result;
+       }
+
+       int out = riscv_resume_all_harts(target);
+       if (out != ERROR_OK) {
+               LOG_ERROR("unable to resume all harts");
+               return out;
+       }
+
+       register_cache_invalidate(target->reg_cache);
+       target->state = TARGET_RUNNING;
+       target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+       return out;
+}
+
+int riscv_openocd_step(
+               struct target *target,
+               int current,
+               target_addr_t address,
+               int handle_breakpoints
+) {
+       LOG_DEBUG("stepping rtos hart");
+
+       if (!current)
+               riscv_set_register(target, GDB_REGNO_PC, address);
+
+       int out = riscv_step_rtos_hart(target);
+       if (out != ERROR_OK) {
+               LOG_ERROR("unable to step rtos hart");
+               return out;
+       }
+
+       register_cache_invalidate(target->reg_cache);
+       target->state = TARGET_RUNNING;
+       target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+       target->state = TARGET_HALTED;
+       target->debug_reason = DBG_REASON_SINGLESTEP;
+       target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+       return out;
+}
+
+/* Command Handlers */
+COMMAND_HANDLER(riscv_set_command_timeout_sec)
+{
+       if (CMD_ARGC != 1) {
+               LOG_ERROR("Command takes exactly 1 parameter");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+       int timeout = atoi(CMD_ARGV[0]);
+       if (timeout <= 0) {
+               LOG_ERROR("%s is not a valid integer argument for command.", CMD_ARGV[0]);
+               return ERROR_FAIL;
+       }
+
+       riscv_command_timeout_sec = timeout;
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_set_reset_timeout_sec)
+{
+       if (CMD_ARGC != 1) {
+               LOG_ERROR("Command takes exactly 1 parameter");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+       int timeout = atoi(CMD_ARGV[0]);
+       if (timeout <= 0) {
+               LOG_ERROR("%s is not a valid integer argument for command.", CMD_ARGV[0]);
+               return ERROR_FAIL;
+       }
+
+       riscv_reset_timeout_sec = timeout;
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_set_prefer_sba)
+{
+       if (CMD_ARGC != 1) {
+               LOG_ERROR("Command takes exactly 1 parameter");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+       COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_prefer_sba);
+       return ERROR_OK;
+}
+
+void parse_error(const char *string, char c, unsigned position)
+{
+       char buf[position+2];
+       for (unsigned i = 0; i < position; i++)
+               buf[i] = ' ';
+       buf[position] = '^';
+       buf[position + 1] = 0;
+
+       LOG_ERROR("Parse error at character %c in:", c);
+       LOG_ERROR("%s", string);
+       LOG_ERROR("%s", buf);
+}
+
+COMMAND_HANDLER(riscv_set_expose_csrs)
+{
+       if (CMD_ARGC != 1) {
+               LOG_ERROR("Command takes exactly 1 parameter");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       for (unsigned pass = 0; pass < 2; pass++) {
+               unsigned range = 0;
+               unsigned low = 0;
+               bool parse_low = true;
+               unsigned high = 0;
+               for (unsigned i = 0; i == 0 || CMD_ARGV[0][i-1]; i++) {
+                       char c = CMD_ARGV[0][i];
+                       if (isspace(c)) {
+                               /* Ignore whitespace. */
+                               continue;
+                       }
+
+                       if (parse_low) {
+                               if (isdigit(c)) {
+                                       low *= 10;
+                                       low += c - '0';
+                               } else if (c == '-') {
+                                       parse_low = false;
+                               } else if (c == ',' || c == 0) {
+                                       if (pass == 1) {
+                                               expose_csr[range].low = low;
+                                               expose_csr[range].high = low;
+                                       }
+                                       low = 0;
+                                       range++;
+                               } else {
+                                       parse_error(CMD_ARGV[0], c, i);
+                                       return ERROR_COMMAND_SYNTAX_ERROR;
+                               }
+
+                       } else {
+                               if (isdigit(c)) {
+                                       high *= 10;
+                                       high += c - '0';
+                               } else if (c == ',' || c == 0) {
+                                       parse_low = true;
+                                       if (pass == 1) {
+                                               expose_csr[range].low = low;
+                                               expose_csr[range].high = high;
+                                       }
+                                       low = 0;
+                                       high = 0;
+                                       range++;
+                               } else {
+                                       parse_error(CMD_ARGV[0], c, i);
+                                       return ERROR_COMMAND_SYNTAX_ERROR;
+                               }
+                       }
+               }
+
+               if (pass == 0) {
+                       if (expose_csr)
+                               free(expose_csr);
+                       expose_csr = calloc(range + 2, sizeof(*expose_csr));
+               } else {
+                       expose_csr[range].low = 1;
+                       expose_csr[range].high = 0;
+               }
+       }
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_authdata_read)
+{
+       if (CMD_ARGC != 0) {
+               LOG_ERROR("Command takes no parameters");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       struct target *target = get_current_target(CMD_CTX);
+       if (!target) {
+               LOG_ERROR("target is NULL!");
+               return ERROR_FAIL;
+       }
+
+       RISCV_INFO(r);
+       if (!r) {
+               LOG_ERROR("riscv_info is NULL!");
+               return ERROR_FAIL;
+       }
+
+       if (r->authdata_read) {
+               uint32_t value;
+               if (r->authdata_read(target, &value) != ERROR_OK)
+                       return ERROR_FAIL;
+               command_print(CMD_CTX, "0x%" PRIx32, value);
+               return ERROR_OK;
+       } else {
+               LOG_ERROR("authdata_read is not implemented for this target.");
+               return ERROR_FAIL;
+       }
+}
+
+COMMAND_HANDLER(riscv_authdata_write)
+{
+       if (CMD_ARGC != 1) {
+               LOG_ERROR("Command takes exactly 1 argument");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(r);
+
+       uint32_t value;
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value);
+
+       if (r->authdata_write) {
+               return r->authdata_write(target, value);
+       } else {
+               LOG_ERROR("authdata_write is not implemented for this target.");
+               return ERROR_FAIL;
+       }
+}
+
+COMMAND_HANDLER(riscv_dmi_read)
+{
+       if (CMD_ARGC != 1) {
+               LOG_ERROR("Command takes 1 parameter");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       struct target *target = get_current_target(CMD_CTX);
+       if (!target) {
+               LOG_ERROR("target is NULL!");
+               return ERROR_FAIL;
+       }
+
+       RISCV_INFO(r);
+       if (!r) {
+               LOG_ERROR("riscv_info is NULL!");
+               return ERROR_FAIL;
+       }
+
+       if (r->dmi_read) {
+               uint32_t address, value;
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
+               if (r->dmi_read(target, &value, address) != ERROR_OK)
+                       return ERROR_FAIL;
+               command_print(CMD_CTX, "0x%" PRIx32, value);
+               return ERROR_OK;
+       } else {
+               LOG_ERROR("dmi_read is not implemented for this target.");
+               return ERROR_FAIL;
+       }
+}
+
+
+COMMAND_HANDLER(riscv_dmi_write)
+{
+       if (CMD_ARGC != 2) {
+               LOG_ERROR("Command takes exactly 2 arguments");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(r);
+
+       uint32_t address, value;
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
+
+       if (r->dmi_write) {
+               return r->dmi_write(target, address, value);
+       } else {
+               LOG_ERROR("dmi_write is not implemented for this target.");
+               return ERROR_FAIL;
+       }
+}
+
+static const struct command_registration riscv_exec_command_handlers[] = {
+       {
+               .name = "set_command_timeout_sec",
+               .handler = riscv_set_command_timeout_sec,
+               .mode = COMMAND_ANY,
+               .usage = "riscv set_command_timeout_sec [sec]",
+               .help = "Set the wall-clock timeout (in seconds) for individual commands"
+       },
+       {
+               .name = "set_reset_timeout_sec",
+               .handler = riscv_set_reset_timeout_sec,
+               .mode = COMMAND_ANY,
+               .usage = "riscv set_reset_timeout_sec [sec]",
+               .help = "Set the wall-clock timeout (in seconds) after reset is deasserted"
+       },
+       {
+               .name = "set_prefer_sba",
+               .handler = riscv_set_prefer_sba,
+               .mode = COMMAND_ANY,
+               .usage = "riscv set_prefer_sba on|off",
+               .help = "When on, prefer to use System Bus Access to access memory. "
+                       "When off, prefer to use the Program Buffer to access memory."
+       },
+       {
+               .name = "expose_csrs",
+               .handler = riscv_set_expose_csrs,
+               .mode = COMMAND_ANY,
+               .usage = "riscv expose_csrs n0[-m0][,n1[-m1]]...",
+               .help = "Configure a list of inclusive ranges for CSRs to expose in "
+                               "addition to the standard ones. This must be executed before "
+                               "`init`."
+       },
+       {
+               .name = "authdata_read",
+               .handler = riscv_authdata_read,
+               .mode = COMMAND_ANY,
+               .usage = "riscv authdata_read",
+               .help = "Return the 32-bit value read from authdata."
+       },
+       {
+               .name = "authdata_write",
+               .handler = riscv_authdata_write,
+               .mode = COMMAND_ANY,
+               .usage = "riscv authdata_write value",
+               .help = "Write the 32-bit value to authdata."
+       },
+       {
+               .name = "dmi_read",
+               .handler = riscv_dmi_read,
+               .mode = COMMAND_ANY,
+               .usage = "riscv dmi_read address",
+               .help = "Perform a 32-bit DMI read at address, returning the value."
+       },
+       {
+               .name = "dmi_write",
+               .handler = riscv_dmi_write,
+               .mode = COMMAND_ANY,
+               .usage = "riscv dmi_write address value",
+               .help = "Perform a 32-bit DMI write of value at address."
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+extern __COMMAND_HANDLER(handle_common_semihosting_command);
+extern __COMMAND_HANDLER(handle_common_semihosting_fileio_command);
+extern __COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command);
+extern __COMMAND_HANDLER(handle_common_semihosting_cmdline);
+
+/*
+ * To be noted that RISC-V targets use the same semihosting commands as
+ * ARM targets.
+ *
+ * The main reason is compatibility with existing tools. For example the
+ * Eclipse OpenOCD/SEGGER J-Link/QEMU plug-ins have several widgets to
+ * configure semihosting, which generate commands like `arm semihosting
+ * enable`.
+ * A secondary reason is the fact that the protocol used is exactly the
+ * one specified by ARM. If RISC-V will ever define its own semihosting
+ * protocol, then a command like `riscv semihosting enable` will make
+ * sense, but for now all semihosting commands are prefixed with `arm`.
+ */
+static const struct command_registration arm_exec_command_handlers[] = {
+       {
+               "semihosting",
+               .handler = handle_common_semihosting_command,
+               .mode = COMMAND_EXEC,
+               .usage = "['enable'|'disable']",
+               .help = "activate support for semihosting operations",
+       },
+       {
+               "semihosting_cmdline",
+               .handler = handle_common_semihosting_cmdline,
+               .mode = COMMAND_EXEC,
+               .usage = "arguments",
+               .help = "command line arguments to be passed to program",
+       },
+       {
+               "semihosting_fileio",
+               .handler = handle_common_semihosting_fileio_command,
+               .mode = COMMAND_EXEC,
+               .usage = "['enable'|'disable']",
+               .help = "activate support for semihosting fileio operations",
+       },
+       {
+               "semihosting_resexit",
+               .handler = handle_common_semihosting_resumable_exit_command,
+               .mode = COMMAND_EXEC,
+               .usage = "['enable'|'disable']",
+               .help = "activate support for semihosting resumable exit",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+const struct command_registration riscv_command_handlers[] = {
+       {
+               .name = "riscv",
+               .mode = COMMAND_ANY,
+               .help = "RISC-V Command Group",
+               .usage = "",
+               .chain = riscv_exec_command_handlers
+       },
+       {
+               .name = "arm",
+               .mode = COMMAND_ANY,
+               .help = "ARM Command Group",
+               .usage = "",
+               .chain = arm_exec_command_handlers
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct target_type riscv_target = {
+       .name = "riscv",
+
+       .init_target = riscv_init_target,
+       .deinit_target = riscv_deinit_target,
+       .examine = riscv_examine,
+
+       /* poll current target status */
+       .poll = old_or_new_riscv_poll,
+
+       .halt = old_or_new_riscv_halt,
+       .resume = old_or_new_riscv_resume,
+       .step = old_or_new_riscv_step,
+
+       .assert_reset = riscv_assert_reset,
+       .deassert_reset = riscv_deassert_reset,
+
+       .read_memory = riscv_read_memory,
+       .write_memory = riscv_write_memory,
+
+       .checksum_memory = riscv_checksum_memory,
+
+       .get_gdb_reg_list = riscv_get_gdb_reg_list,
+
+       .add_breakpoint = riscv_add_breakpoint,
+       .remove_breakpoint = riscv_remove_breakpoint,
+
+       .add_watchpoint = riscv_add_watchpoint,
+       .remove_watchpoint = riscv_remove_watchpoint,
+
+       .arch_state = riscv_arch_state,
+
+       .run_algorithm = riscv_run_algorithm,
+
+       .commands = riscv_command_handlers
+};
+
+/*** RISC-V Interface ***/
+
+void riscv_info_init(struct target *target, riscv_info_t *r)
+{
+       memset(r, 0, sizeof(*r));
+       r->dtm_version = 1;
+       r->registers_initialized = false;
+       r->current_hartid = target->coreid;
+
+       memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id));
+
+       for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) {
+               r->xlen[h] = -1;
+
+               for (size_t e = 0; e < RISCV_MAX_REGISTERS; ++e)
+                       r->valid_saved_registers[h][e] = false;
+       }
+}
+
+int riscv_halt_all_harts(struct target *target)
+{
+       for (int i = 0; i < riscv_count_harts(target); ++i) {
+               if (!riscv_hart_enabled(target, i))
+                       continue;
+
+               riscv_halt_one_hart(target, i);
+       }
+
+       return ERROR_OK;
+}
+
+int riscv_halt_one_hart(struct target *target, int hartid)
+{
+       RISCV_INFO(r);
+       LOG_DEBUG("halting hart %d", hartid);
+       if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+               return ERROR_FAIL;
+       if (riscv_is_halted(target)) {
+               LOG_DEBUG("  hart %d requested halt, but was already halted", hartid);
+               return ERROR_OK;
+       }
+
+       return r->halt_current_hart(target);
+}
+
+int riscv_resume_all_harts(struct target *target)
+{
+       for (int i = 0; i < riscv_count_harts(target); ++i) {
+               if (!riscv_hart_enabled(target, i))
+                       continue;
+
+               riscv_resume_one_hart(target, i);
+       }
+
+       riscv_invalidate_register_cache(target);
+       return ERROR_OK;
+}
+
+int riscv_resume_one_hart(struct target *target, int hartid)
+{
+       RISCV_INFO(r);
+       LOG_DEBUG("resuming hart %d", hartid);
+       if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+               return ERROR_FAIL;
+       if (!riscv_is_halted(target)) {
+               LOG_DEBUG("  hart %d requested resume, but was already resumed", hartid);
+               return ERROR_OK;
+       }
+
+       r->on_resume(target);
+       return r->resume_current_hart(target);
+}
+
+int riscv_step_rtos_hart(struct target *target)
+{
+       RISCV_INFO(r);
+       int hartid = r->current_hartid;
+       if (riscv_rtos_enabled(target)) {
+               hartid = r->rtos_hartid;
+               if (hartid == -1) {
+                       LOG_USER("GDB has asked me to step \"any\" thread, so I'm stepping hart 0.");
+                       hartid = 0;
+               }
+       }
+       if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+               return ERROR_FAIL;
+       LOG_DEBUG("stepping hart %d", hartid);
+
+       if (!riscv_is_halted(target)) {
+               LOG_ERROR("Hart isn't halted before single step!");
+               return ERROR_FAIL;
+       }
+       riscv_invalidate_register_cache(target);
+       r->on_step(target);
+       if (r->step_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+       riscv_invalidate_register_cache(target);
+       r->on_halt(target);
+       if (!riscv_is_halted(target)) {
+               LOG_ERROR("Hart was not halted after single step!");
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
+bool riscv_supports_extension(struct target *target, int hartid, char letter)
+{
+       RISCV_INFO(r);
+       unsigned num;
+       if (letter >= 'a' && letter <= 'z')
+               num = letter - 'a';
+       else if (letter >= 'A' && letter <= 'Z')
+               num = letter - 'A';
+       else
+               return false;
+       return r->misa[hartid] & (1 << num);
+}
+
+int riscv_xlen(const struct target *target)
+{
+       return riscv_xlen_of_hart(target, riscv_current_hartid(target));
+}
+
+int riscv_xlen_of_hart(const struct target *target, int hartid)
+{
+       RISCV_INFO(r);
+       assert(r->xlen[hartid] != -1);
+       return r->xlen[hartid];
+}
+
+bool riscv_rtos_enabled(const struct target *target)
+{
+       return target->rtos != NULL;
+}
+
+int riscv_set_current_hartid(struct target *target, int hartid)
+{
+       RISCV_INFO(r);
+       if (!r->select_current_hart)
+               return ERROR_OK;
+
+       int previous_hartid = riscv_current_hartid(target);
+       r->current_hartid = hartid;
+       assert(riscv_hart_enabled(target, hartid));
+       LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid);
+       if (r->select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* This might get called during init, in which case we shouldn't be
+        * setting up the register cache. */
+       if (!target_was_examined(target))
+               return ERROR_OK;
+
+       /* Avoid invalidating the register cache all the time. */
+       if (r->registers_initialized
+                       && (!riscv_rtos_enabled(target) || (previous_hartid == hartid))
+                       && target->reg_cache->reg_list[GDB_REGNO_ZERO].size == (unsigned)riscv_xlen(target)
+                       && (!riscv_rtos_enabled(target) || (r->rtos_hartid != -1))) {
+               return ERROR_OK;
+       } else
+               LOG_DEBUG("Initializing registers: xlen=%d", riscv_xlen(target));
+
+       riscv_invalidate_register_cache(target);
+       return ERROR_OK;
+}
+
+void riscv_invalidate_register_cache(struct target *target)
+{
+       RISCV_INFO(r);
+
+       register_cache_invalidate(target->reg_cache);
+       for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) {
+               struct reg *reg = &target->reg_cache->reg_list[i];
+               reg->valid = false;
+       }
+
+       r->registers_initialized = true;
+}
+
+int riscv_current_hartid(const struct target *target)
+{
+       RISCV_INFO(r);
+       return r->current_hartid;
+}
+
+void riscv_set_all_rtos_harts(struct target *target)
+{
+       RISCV_INFO(r);
+       r->rtos_hartid = -1;
+}
+
+void riscv_set_rtos_hartid(struct target *target, int hartid)
+{
+       LOG_DEBUG("setting RTOS hartid %d", hartid);
+       RISCV_INFO(r);
+       r->rtos_hartid = hartid;
+}
+
+int riscv_count_harts(struct target *target)
+{
+       if (target == NULL)
+               return 1;
+       RISCV_INFO(r);
+       if (r == NULL)
+               return 1;
+       return r->hart_count;
+}
+
+bool riscv_has_register(struct target *target, int hartid, int regid)
+{
+       return 1;
+}
+
+/**
+ * This function is called when the debug user wants to change the value of a
+ * register. The new value may be cached, and may not be written until the hart
+ * is resumed. */
+int riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v)
+{
+       return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v);
+}
+
+int riscv_set_register_on_hart(struct target *target, int hartid,
+               enum gdb_regno regid, uint64_t value)
+{
+       RISCV_INFO(r);
+       LOG_DEBUG("[%d] %s <- %" PRIx64, hartid, gdb_regno_name(regid), value);
+       assert(r->set_register);
+       return r->set_register(target, hartid, regid, value);
+}
+
+int riscv_get_register(struct target *target, riscv_reg_t *value,
+               enum gdb_regno r)
+{
+       return riscv_get_register_on_hart(target, value,
+                       riscv_current_hartid(target), r);
+}
+
+int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value,
+               int hartid, enum gdb_regno regid)
+{
+       RISCV_INFO(r);
+       int result = r->get_register(target, value, hartid, regid);
+       LOG_DEBUG("[%d] %s: %" PRIx64, hartid, gdb_regno_name(regid), *value);
+       return result;
+}
+
+bool riscv_is_halted(struct target *target)
+{
+       RISCV_INFO(r);
+       assert(r->is_halted);
+       return r->is_halted(target);
+}
+
+enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid)
+{
+       RISCV_INFO(r);
+       if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+               return RISCV_HALT_ERROR;
+       if (!riscv_is_halted(target)) {
+               LOG_ERROR("Hart is not halted!");
+               return RISCV_HALT_UNKNOWN;
+       }
+       return r->halt_reason(target);
+}
+
+size_t riscv_debug_buffer_size(struct target *target)
+{
+       RISCV_INFO(r);
+       return r->debug_buffer_size[riscv_current_hartid(target)];
+}
+
+int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn)
+{
+       RISCV_INFO(r);
+       r->write_debug_buffer(target, index, insn);
+       return ERROR_OK;
+}
+
+riscv_insn_t riscv_read_debug_buffer(struct target *target, int index)
+{
+       RISCV_INFO(r);
+       return r->read_debug_buffer(target, index);
+}
+
+int riscv_execute_debug_buffer(struct target *target)
+{
+       RISCV_INFO(r);
+       return r->execute_debug_buffer(target);
+}
+
+void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d)
+{
+       RISCV_INFO(r);
+       r->fill_dmi_write_u64(target, buf, a, d);
+}
+
+void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a)
+{
+       RISCV_INFO(r);
+       r->fill_dmi_read_u64(target, buf, a);
+}
+
+void riscv_fill_dmi_nop_u64(struct target *target, char *buf)
+{
+       RISCV_INFO(r);
+       r->fill_dmi_nop_u64(target, buf);
+}
+
+int riscv_dmi_write_u64_bits(struct target *target)
+{
+       RISCV_INFO(r);
+       return r->dmi_write_u64_bits(target);
+}
+
+bool riscv_hart_enabled(struct target *target, int hartid)
+{
+       /* FIXME: Add a hart mask to the RTOS. */
+       if (riscv_rtos_enabled(target))
+               return hartid < riscv_count_harts(target);
+
+       return hartid == target->coreid;
+}
+
+/**
+ * Count triggers, and initialize trigger_count for each hart.
+ * trigger_count is initialized even if this function fails to discover
+ * something.
+ * Disable any hardware triggers that have dmode set. We can't have set them
+ * ourselves. Maybe they're left over from some killed debug session.
+ * */
+int riscv_enumerate_triggers(struct target *target)
+{
+       RISCV_INFO(r);
+
+       if (r->triggers_enumerated)
+               return ERROR_OK;
+
+       r->triggers_enumerated = true;  /* At the very least we tried. */
+
+       for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
+               if (!riscv_hart_enabled(target, hartid))
+                       continue;
+
+               riscv_reg_t tselect;
+               int result = riscv_get_register_on_hart(target, &tselect, hartid,
+                               GDB_REGNO_TSELECT);
+               if (result != ERROR_OK)
+                       return result;
+
+               for (unsigned t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
+                       r->trigger_count[hartid] = t;
+
+                       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, t);
+                       uint64_t tselect_rb;
+                       result = riscv_get_register_on_hart(target, &tselect_rb, hartid,
+                                       GDB_REGNO_TSELECT);
+                       if (result != ERROR_OK)
+                               return result;
+                       /* Mask off the top bit, which is used as tdrmode in old
+                        * implementations. */
+                       tselect_rb &= ~(1ULL << (riscv_xlen(target)-1));
+                       if (tselect_rb != t)
+                               break;
+                       uint64_t tdata1;
+                       result = riscv_get_register_on_hart(target, &tdata1, hartid,
+                                       GDB_REGNO_TDATA1);
+                       if (result != ERROR_OK)
+                               return result;
+
+                       int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
+                       switch (type) {
+                               case 1:
+                                       /* On these older cores we don't support software using
+                                        * triggers. */
+                                       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
+                                       break;
+                               case 2:
+                                       if (tdata1 & MCONTROL_DMODE(riscv_xlen(target)))
+                                               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
+                                       break;
+                       }
+               }
+
+               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect);
+
+               LOG_INFO("[%d] Found %d triggers", hartid, r->trigger_count[hartid]);
+       }
+
+       return ERROR_OK;
+}
+
+const char *gdb_regno_name(enum gdb_regno regno)
+{
+       static char buf[32];
+
+       switch (regno) {
+               case GDB_REGNO_ZERO:
+                       return "zero";
+               case GDB_REGNO_S0:
+                       return "s0";
+               case GDB_REGNO_S1:
+                       return "s1";
+               case GDB_REGNO_PC:
+                       return "pc";
+               case GDB_REGNO_FPR0:
+                       return "fpr0";
+               case GDB_REGNO_FPR31:
+                       return "fpr31";
+               case GDB_REGNO_CSR0:
+                       return "csr0";
+               case GDB_REGNO_TSELECT:
+                       return "tselect";
+               case GDB_REGNO_TDATA1:
+                       return "tdata1";
+               case GDB_REGNO_TDATA2:
+                       return "tdata2";
+               case GDB_REGNO_MISA:
+                       return "misa";
+               case GDB_REGNO_DPC:
+                       return "dpc";
+               case GDB_REGNO_DCSR:
+                       return "dcsr";
+               case GDB_REGNO_DSCRATCH:
+                       return "dscratch";
+               case GDB_REGNO_MSTATUS:
+                       return "mstatus";
+               case GDB_REGNO_PRIV:
+                       return "priv";
+               default:
+                       if (regno <= GDB_REGNO_XPR31)
+                               sprintf(buf, "x%d", regno - GDB_REGNO_ZERO);
+                       else if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095)
+                               sprintf(buf, "csr%d", regno - GDB_REGNO_CSR0);
+                       else if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31)
+                               sprintf(buf, "f%d", regno - GDB_REGNO_FPR0);
+                       else
+                               sprintf(buf, "gdb_regno_%d", regno);
+                       return buf;
+       }
+}
+
+static int register_get(struct reg *reg)
+{
+       struct target *target = (struct target *) reg->arch_info;
+       uint64_t value;
+       int result = riscv_get_register(target, &value, reg->number);
+       if (result != ERROR_OK)
+               return result;
+       buf_set_u64(reg->value, 0, reg->size, value);
+       return ERROR_OK;
+}
+
+static int register_set(struct reg *reg, uint8_t *buf)
+{
+       struct target *target = (struct target *) reg->arch_info;
+
+       uint64_t value = buf_get_u64(buf, 0, reg->size);
+
+       LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name);
+       struct reg *r = &target->reg_cache->reg_list[reg->number];
+       r->valid = true;
+       memcpy(r->value, buf, (r->size + 7) / 8);
+
+       riscv_set_register(target, reg->number, value);
+       return ERROR_OK;
+}
+
+static struct reg_arch_type riscv_reg_arch_type = {
+       .get = register_get,
+       .set = register_set
+};
+
+struct csr_info {
+       unsigned number;
+       const char *name;
+};
+
+static int cmp_csr_info(const void *p1, const void *p2)
+{
+       return (int) (((struct csr_info *)p1)->number) - (int) (((struct csr_info *)p2)->number);
+}
+
+int riscv_init_registers(struct target *target)
+{
+       RISCV_INFO(info);
+
+       if (target->reg_cache) {
+               if (target->reg_cache->reg_list)
+                       free(target->reg_cache->reg_list);
+               free(target->reg_cache);
+       }
+
+       target->reg_cache = calloc(1, sizeof(*target->reg_cache));
+       target->reg_cache->name = "RISC-V Registers";
+       target->reg_cache->num_regs = GDB_REGNO_COUNT;
+
+       target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg));
+
+       const unsigned int max_reg_name_len = 12;
+       if (info->reg_names)
+               free(info->reg_names);
+       info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len);
+       char *reg_name = info->reg_names;
+
+       static struct reg_feature feature_cpu = {
+               .name = "org.gnu.gdb.riscv.cpu"
+       };
+       static struct reg_feature feature_fpu = {
+               .name = "org.gnu.gdb.riscv.fpu"
+       };
+       static struct reg_feature feature_csr = {
+               .name = "org.gnu.gdb.riscv.csr"
+       };
+       static struct reg_feature feature_virtual = {
+               .name = "org.gnu.gdb.riscv.virtual"
+       };
+
+       static struct reg_data_type type_ieee_single = {
+               .type = REG_TYPE_IEEE_SINGLE,
+               .id = "ieee_single"
+       };
+       static struct reg_data_type type_ieee_double = {
+               .type = REG_TYPE_IEEE_DOUBLE,
+               .id = "ieee_double"
+       };
+       struct csr_info csr_info[] = {
+#define DECLARE_CSR(name, number) { number, #name },
+#include "encoding.h"
+#undef DECLARE_CSR
+       };
+       /* encoding.h does not contain the registers in sorted order. */
+       qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info);
+       unsigned csr_info_index = 0;
+
+       /* When gdb request register N, gdb_get_register_packet() assumes that this
+        * is register at index N in reg_list. So if there are certain registers
+        * that don't exist, we need to leave holes in the list (or renumber, but
+        * it would be nice not to have yet another set of numbers to translate
+        * between). */
+       for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) {
+               struct reg *r = &target->reg_cache->reg_list[number];
+               r->dirty = false;
+               r->valid = false;
+               r->exist = true;
+               r->type = &riscv_reg_arch_type;
+               r->arch_info = target;
+               r->number = number;
+               r->size = riscv_xlen(target);
+               /* r->size is set in riscv_invalidate_register_cache, maybe because the
+                * target is in theory allowed to change XLEN on us. But I expect a lot
+                * of other things to break in that case as well. */
+               if (number <= GDB_REGNO_XPR31) {
+                       r->caller_save = true;
+                       switch (number) {
+                               case GDB_REGNO_ZERO:
+                                       r->name = "zero";
+                                       break;
+                               case GDB_REGNO_RA:
+                                       r->name = "ra";
+                                       break;
+                               case GDB_REGNO_SP:
+                                       r->name = "sp";
+                                       break;
+                               case GDB_REGNO_GP:
+                                       r->name = "gp";
+                                       break;
+                               case GDB_REGNO_TP:
+                                       r->name = "tp";
+                                       break;
+                               case GDB_REGNO_T0:
+                                       r->name = "t0";
+                                       break;
+                               case GDB_REGNO_T1:
+                                       r->name = "t1";
+                                       break;
+                               case GDB_REGNO_T2:
+                                       r->name = "t2";
+                                       break;
+                               case GDB_REGNO_FP:
+                                       r->name = "fp";
+                                       break;
+                               case GDB_REGNO_S1:
+                                       r->name = "s1";
+                                       break;
+                               case GDB_REGNO_A0:
+                                       r->name = "a0";
+                                       break;
+                               case GDB_REGNO_A1:
+                                       r->name = "a1";
+                                       break;
+                               case GDB_REGNO_A2:
+                                       r->name = "a2";
+                                       break;
+                               case GDB_REGNO_A3:
+                                       r->name = "a3";
+                                       break;
+                               case GDB_REGNO_A4:
+                                       r->name = "a4";
+                                       break;
+                               case GDB_REGNO_A5:
+                                       r->name = "a5";
+                                       break;
+                               case GDB_REGNO_A6:
+                                       r->name = "a6";
+                                       break;
+                               case GDB_REGNO_A7:
+                                       r->name = "a7";
+                                       break;
+                               case GDB_REGNO_S2:
+                                       r->name = "s2";
+                                       break;
+                               case GDB_REGNO_S3:
+                                       r->name = "s3";
+                                       break;
+                               case GDB_REGNO_S4:
+                                       r->name = "s4";
+                                       break;
+                               case GDB_REGNO_S5:
+                                       r->name = "s5";
+                                       break;
+                               case GDB_REGNO_S6:
+                                       r->name = "s6";
+                                       break;
+                               case GDB_REGNO_S7:
+                                       r->name = "s7";
+                                       break;
+                               case GDB_REGNO_S8:
+                                       r->name = "s8";
+                                       break;
+                               case GDB_REGNO_S9:
+                                       r->name = "s9";
+                                       break;
+                               case GDB_REGNO_S10:
+                                       r->name = "s10";
+                                       break;
+                               case GDB_REGNO_S11:
+                                       r->name = "s11";
+                                       break;
+                               case GDB_REGNO_T3:
+                                       r->name = "t3";
+                                       break;
+                               case GDB_REGNO_T4:
+                                       r->name = "t4";
+                                       break;
+                               case GDB_REGNO_T5:
+                                       r->name = "t5";
+                                       break;
+                               case GDB_REGNO_T6:
+                                       r->name = "t6";
+                                       break;
+                       }
+                       r->group = "general";
+                       r->feature = &feature_cpu;
+               } else if (number == GDB_REGNO_PC) {
+                       r->caller_save = true;
+                       sprintf(reg_name, "pc");
+                       r->group = "general";
+                       r->feature = &feature_cpu;
+               } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
+                       r->caller_save = true;
+                       if (riscv_supports_extension(target, riscv_current_hartid(target),
+                                               'D')) {
+                               r->reg_data_type = &type_ieee_double;
+                               r->size = 64;
+                       } else if (riscv_supports_extension(target,
+                                               riscv_current_hartid(target), 'F')) {
+                               r->reg_data_type = &type_ieee_single;
+                               r->size = 32;
+                       } else {
+                               r->exist = false;
+                       }
+                       switch (number) {
+                               case GDB_REGNO_FT0:
+                                       r->name = "ft0";
+                                       break;
+                               case GDB_REGNO_FT1:
+                                       r->name = "ft1";
+                                       break;
+                               case GDB_REGNO_FT2:
+                                       r->name = "ft2";
+                                       break;
+                               case GDB_REGNO_FT3:
+                                       r->name = "ft3";
+                                       break;
+                               case GDB_REGNO_FT4:
+                                       r->name = "ft4";
+                                       break;
+                               case GDB_REGNO_FT5:
+                                       r->name = "ft5";
+                                       break;
+                               case GDB_REGNO_FT6:
+                                       r->name = "ft6";
+                                       break;
+                               case GDB_REGNO_FT7:
+                                       r->name = "ft7";
+                                       break;
+                               case GDB_REGNO_FS0:
+                                       r->name = "fs0";
+                                       break;
+                               case GDB_REGNO_FS1:
+                                       r->name = "fs1";
+                                       break;
+                               case GDB_REGNO_FA0:
+                                       r->name = "fa0";
+                                       break;
+                               case GDB_REGNO_FA1:
+                                       r->name = "fa1";
+                                       break;
+                               case GDB_REGNO_FA2:
+                                       r->name = "fa2";
+                                       break;
+                               case GDB_REGNO_FA3:
+                                       r->name = "fa3";
+                                       break;
+                               case GDB_REGNO_FA4:
+                                       r->name = "fa4";
+                                       break;
+                               case GDB_REGNO_FA5:
+                                       r->name = "fa5";
+                                       break;
+                               case GDB_REGNO_FA6:
+                                       r->name = "fa6";
+                                       break;
+                               case GDB_REGNO_FA7:
+                                       r->name = "fa7";
+                                       break;
+                               case GDB_REGNO_FS2:
+                                       r->name = "fs2";
+                                       break;
+                               case GDB_REGNO_FS3:
+                                       r->name = "fs3";
+                                       break;
+                               case GDB_REGNO_FS4:
+                                       r->name = "fs4";
+                                       break;
+                               case GDB_REGNO_FS5:
+                                       r->name = "fs5";
+                                       break;
+                               case GDB_REGNO_FS6:
+                                       r->name = "fs6";
+                                       break;
+                               case GDB_REGNO_FS7:
+                                       r->name = "fs7";
+                                       break;
+                               case GDB_REGNO_FS8:
+                                       r->name = "fs8";
+                                       break;
+                               case GDB_REGNO_FS9:
+                                       r->name = "fs9";
+                                       break;
+                               case GDB_REGNO_FS10:
+                                       r->name = "fs10";
+                                       break;
+                               case GDB_REGNO_FS11:
+                                       r->name = "fs11";
+                                       break;
+                               case GDB_REGNO_FT8:
+                                       r->name = "ft8";
+                                       break;
+                               case GDB_REGNO_FT9:
+                                       r->name = "ft9";
+                                       break;
+                               case GDB_REGNO_FT10:
+                                       r->name = "ft10";
+                                       break;
+                               case GDB_REGNO_FT11:
+                                       r->name = "ft11";
+                                       break;
+                       }
+                       r->group = "float";
+                       r->feature = &feature_fpu;
+               } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
+                       r->group = "csr";
+                       r->feature = &feature_csr;
+                       unsigned csr_number = number - GDB_REGNO_CSR0;
+
+                       while (csr_info[csr_info_index].number < csr_number &&
+                                       csr_info_index < DIM(csr_info) - 1) {
+                               csr_info_index++;
+                       }
+                       if (csr_info[csr_info_index].number == csr_number) {
+                               r->name = csr_info[csr_info_index].name;
+                       } else {
+                               sprintf(reg_name, "csr%d", csr_number);
+                               /* Assume unnamed registers don't exist, unless we have some
+                                * configuration that tells us otherwise. That's important
+                                * because eg. Eclipse crashes if a target has too many
+                                * registers, and apparently has no way of only showing a
+                                * subset of registers in any case. */
+                               r->exist = false;
+                       }
+
+                       switch (csr_number) {
+                               case CSR_FFLAGS:
+                               case CSR_FRM:
+                               case CSR_FCSR:
+                                       r->exist = riscv_supports_extension(target,
+                                                       riscv_current_hartid(target), 'F');
+                                       r->group = "float";
+                                       r->feature = &feature_fpu;
+                                       break;
+                               case CSR_SSTATUS:
+                               case CSR_STVEC:
+                               case CSR_SIP:
+                               case CSR_SIE:
+                               case CSR_SCOUNTEREN:
+                               case CSR_SSCRATCH:
+                               case CSR_SEPC:
+                               case CSR_SCAUSE:
+                               case CSR_STVAL:
+                               case CSR_SATP:
+                                       r->exist = riscv_supports_extension(target,
+                                                       riscv_current_hartid(target), 'S');
+                                       break;
+                               case CSR_MEDELEG:
+                               case CSR_MIDELEG:
+                                       /* "In systems with only M-mode, or with both M-mode and
+                                        * U-mode but without U-mode trap support, the medeleg and
+                                        * mideleg registers should not exist." */
+                                       r->exist = riscv_supports_extension(target, riscv_current_hartid(target), 'S') ||
+                                               riscv_supports_extension(target, riscv_current_hartid(target), 'N');
+                                       break;
+
+                               case CSR_CYCLEH:
+                               case CSR_TIMEH:
+                               case CSR_INSTRETH:
+                               case CSR_HPMCOUNTER3H:
+                               case CSR_HPMCOUNTER4H:
+                               case CSR_HPMCOUNTER5H:
+                               case CSR_HPMCOUNTER6H:
+                               case CSR_HPMCOUNTER7H:
+                               case CSR_HPMCOUNTER8H:
+                               case CSR_HPMCOUNTER9H:
+                               case CSR_HPMCOUNTER10H:
+                               case CSR_HPMCOUNTER11H:
+                               case CSR_HPMCOUNTER12H:
+                               case CSR_HPMCOUNTER13H:
+                               case CSR_HPMCOUNTER14H:
+                               case CSR_HPMCOUNTER15H:
+                               case CSR_HPMCOUNTER16H:
+                               case CSR_HPMCOUNTER17H:
+                               case CSR_HPMCOUNTER18H:
+                               case CSR_HPMCOUNTER19H:
+                               case CSR_HPMCOUNTER20H:
+                               case CSR_HPMCOUNTER21H:
+                               case CSR_HPMCOUNTER22H:
+                               case CSR_HPMCOUNTER23H:
+                               case CSR_HPMCOUNTER24H:
+                               case CSR_HPMCOUNTER25H:
+                               case CSR_HPMCOUNTER26H:
+                               case CSR_HPMCOUNTER27H:
+                               case CSR_HPMCOUNTER28H:
+                               case CSR_HPMCOUNTER29H:
+                               case CSR_HPMCOUNTER30H:
+                               case CSR_HPMCOUNTER31H:
+                               case CSR_MCYCLEH:
+                               case CSR_MINSTRETH:
+                               case CSR_MHPMCOUNTER3H:
+                               case CSR_MHPMCOUNTER4H:
+                               case CSR_MHPMCOUNTER5H:
+                               case CSR_MHPMCOUNTER6H:
+                               case CSR_MHPMCOUNTER7H:
+                               case CSR_MHPMCOUNTER8H:
+                               case CSR_MHPMCOUNTER9H:
+                               case CSR_MHPMCOUNTER10H:
+                               case CSR_MHPMCOUNTER11H:
+                               case CSR_MHPMCOUNTER12H:
+                               case CSR_MHPMCOUNTER13H:
+                               case CSR_MHPMCOUNTER14H:
+                               case CSR_MHPMCOUNTER15H:
+                               case CSR_MHPMCOUNTER16H:
+                               case CSR_MHPMCOUNTER17H:
+                               case CSR_MHPMCOUNTER18H:
+                               case CSR_MHPMCOUNTER19H:
+                               case CSR_MHPMCOUNTER20H:
+                               case CSR_MHPMCOUNTER21H:
+                               case CSR_MHPMCOUNTER22H:
+                               case CSR_MHPMCOUNTER23H:
+                               case CSR_MHPMCOUNTER24H:
+                               case CSR_MHPMCOUNTER25H:
+                               case CSR_MHPMCOUNTER26H:
+                               case CSR_MHPMCOUNTER27H:
+                               case CSR_MHPMCOUNTER28H:
+                               case CSR_MHPMCOUNTER29H:
+                               case CSR_MHPMCOUNTER30H:
+                               case CSR_MHPMCOUNTER31H:
+                                       r->exist = riscv_xlen(target) == 32;
+                                       break;
+                       }
+
+                       if (!r->exist && expose_csr) {
+                               for (unsigned i = 0; expose_csr[i].low <= expose_csr[i].high; i++) {
+                                       if (csr_number >= expose_csr[i].low && csr_number <= expose_csr[i].high) {
+                                               LOG_INFO("Exposing additional CSR %d", csr_number);
+                                               r->exist = true;
+                                               break;
+                                       }
+                               }
+                       }
+
+               } else if (number == GDB_REGNO_PRIV) {
+                       sprintf(reg_name, "priv");
+                       r->group = "general";
+                       r->feature = &feature_virtual;
+                       r->size = 8;
+               }
+               if (reg_name[0])
+                       r->name = reg_name;
+               reg_name += strlen(reg_name) + 1;
+               assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len);
+               r->value = &info->reg_cache_values[number];
+       }
+
+       return ERROR_OK;
+}
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
new file mode 100644 (file)
index 0000000..31f3cf6
--- /dev/null
@@ -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
diff --git a/src/target/riscv/riscv_semihosting.c b/src/target/riscv/riscv_semihosting.c
new file mode 100644 (file)
index 0000000..c4b6653
--- /dev/null
@@ -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;
+}
index 591b9ea2c8ee04bee0962a827aef685e55c5f149..68f93210a5e400f6122b2147f70f34f641db08f1 100644 (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
diff --git a/tcl/board/sifive-e31arty.cfg b/tcl/board/sifive-e31arty.cfg
new file mode 100644 (file)
index 0000000..ec10b27
--- /dev/null
@@ -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"
diff --git a/tcl/board/sifive-e51arty.cfg b/tcl/board/sifive-e51arty.cfg
new file mode 100644 (file)
index 0000000..ffd83a0
--- /dev/null
@@ -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"
diff --git a/tcl/board/sifive-hifive1.cfg b/tcl/board/sifive-hifive1.cfg
new file mode 100644 (file)
index 0000000..9bc6670
--- /dev/null
@@ -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

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)