Upstream a whole host of RISC-V changes.
[fw/openocd] / src / target / riscv / riscv-013.c
index fdebdd413d9ff0b00960c9a8af210042476e69b2..006266a90c5e93190d2859013f9ed11c621e4074 100644 (file)
@@ -29,9 +29,6 @@
 #include "asm.h"
 #include "batch.h"
 
-#define DM_DATA1 (DM_DATA0 + 1)
-#define DM_PROGBUF1 (DM_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, bool use_hasel);
@@ -39,8 +36,8 @@ 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);
+               riscv_reg_t *value, int rid);
+static int riscv013_set_register(struct target *target, int regid, uint64_t value);
 static int riscv013_select_current_hart(struct target *target);
 static int riscv013_halt_prep(struct target *target);
 static int riscv013_halt_go(struct target *target);
@@ -74,7 +71,6 @@ void write_memory_sba_simple(struct target *target, target_addr_t addr, uint32_t
                uint32_t write_size, uint32_t sbcs);
 void read_memory_sba_simple(struct target *target, target_addr_t addr,
                uint32_t *rd_buf, uint32_t read_size, uint32_t sbcs);
-static int     riscv013_test_compliance(struct target *target);
 
 /**
  * Since almost everything can be accomplish by scanning the dbus register, all
@@ -206,6 +202,8 @@ typedef struct {
        bool abstract_read_fpr_supported;
        bool abstract_write_fpr_supported;
 
+       yes_no_maybe_t has_aampostincrement;
+
        /* 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.) */
@@ -228,6 +226,8 @@ LIST_HEAD(dm_list);
 static riscv013_info_t *get_info(const struct target *target)
 {
        riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       assert(info);
+       assert(info->version_specific);
        return (riscv013_info_t *) info->version_specific;
 }
 
@@ -588,6 +588,8 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
                        return ERROR_FAIL;
        }
 
+       keep_alive();
+
        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. */
@@ -690,7 +692,7 @@ int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus,
                return result;
        int dmstatus_version = get_field(*dmstatus, DM_DMSTATUS_VERSION);
        if (dmstatus_version != 2 && dmstatus_version != 3) {
-               LOG_ERROR("OpenOCD only supports Debug Module version 2 (0.13) and 3 (0.14), not "
+               LOG_ERROR("OpenOCD only supports Debug Module version 2 (0.13) and 3 (1.0), not "
                                "%d (dmstatus=0x%x). This error might be caused by a JTAG "
                                "signal issue. Try reducing the JTAG clock speed.",
                                get_field(*dmstatus, DM_DMSTATUS_VERSION), *dmstatus);
@@ -1324,7 +1326,7 @@ static int register_write_direct(struct target *target, unsigned number,
        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_supports_extension(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. */
@@ -1354,7 +1356,7 @@ static int register_write_direct(struct target *target, unsigned number,
                        return ERROR_FAIL;
 
                if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
-                       if (riscv_supports_extension(target, riscv_current_hartid(target), 'D'))
+                       if (riscv_supports_extension(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));
@@ -1395,7 +1397,7 @@ static int register_write_direct(struct target *target, unsigned number,
        return exec_out;
 }
 
-/** Return the cached value, or read from the target if necessary. */
+/** Read register value from the target. Also update the cached value. */
 static int register_read(struct target *target, uint64_t *value, uint32_t number)
 {
        if (number == GDB_REGNO_ZERO) {
@@ -1438,7 +1440,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
                        return ERROR_FAIL;
 
                if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
-                       if (riscv_supports_extension(target, riscv_current_hartid(target), 'D')
+                       if (riscv_supports_extension(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. */
@@ -1454,8 +1456,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
                                        scratch_release(target, &scratch);
                                        return ERROR_FAIL;
                                }
-                       } else if (riscv_supports_extension(target,
-                                               riscv_current_hartid(target), 'D')) {
+                       } else if (riscv_supports_extension(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));
@@ -1498,7 +1499,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
        return result;
 }
 
-int wait_for_authbusy(struct target *target, uint32_t *dmstatus)
+static int wait_for_authbusy(struct target *target, uint32_t *dmstatus)
 {
        time_t start = time(NULL);
        while (1) {
@@ -1544,7 +1545,7 @@ static int set_haltgroup(struct target *target, bool *supported)
        return ERROR_OK;
 }
 
-static int discover_vlenb(struct target *target, int hartid)
+static int discover_vlenb(struct target *target)
 {
        RISCV_INFO(r);
        riscv_reg_t vlenb;
@@ -1552,12 +1553,12 @@ static int discover_vlenb(struct target *target, int hartid)
        if (register_read(target, &vlenb, GDB_REGNO_VLENB) != ERROR_OK) {
                LOG_WARNING("Couldn't read vlenb for %s; vector register access won't work.",
                                target_name(target));
-               r->vlenb[hartid] = 0;
+               r->vlenb = 0;
                return ERROR_OK;
        }
-       r->vlenb[hartid] = vlenb;
+       r->vlenb = vlenb;
 
-       LOG_INFO("hart %d: Vector support with vlenb=%d", hartid, r->vlenb[hartid]);
+       LOG_INFO("Vector support with vlenb=%d", r->vlenb);
 
        return ERROR_OK;
 }
@@ -1705,6 +1706,8 @@ static int examine(struct target *target)
                LOG_DEBUG("Detected %d harts.", dm->hart_count);
        }
 
+       r->current_hartid = target->coreid;
+
        if (dm->hart_count == 0) {
                LOG_ERROR("No harts found!");
                return ERROR_FAIL;
@@ -1712,54 +1715,49 @@ static int examine(struct target *target)
 
        /* Don't call any riscv_* functions until after we've counted the number of
         * cores and initialized registers. */
-       for (int i = 0; i < dm->hart_count; ++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;
+       if (riscv013_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
 
-               bool halted = riscv_is_halted(target);
-               if (!halted) {
-                       if (riscv013_halt_go(target) != ERROR_OK) {
-                               LOG_ERROR("Fatal: Hart %d failed to halt during examine()", i);
-                               return ERROR_FAIL;
-                       }
+       bool halted = riscv_is_halted(target);
+       if (!halted) {
+               if (riscv013_halt_go(target) != ERROR_OK) {
+                       LOG_ERROR("Fatal: Hart %d failed to halt during examine()", r->current_hartid);
+                       return ERROR_FAIL;
                }
+       }
 
-               /* Without knowing anything else we can at least mess with the
-                * program buffer. */
-               r->debug_buffer_size[i] = info->progbufsize;
+       /* Without knowing anything else we can at least mess with the
+               * program buffer. */
+       r->debug_buffer_size = 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;
-               }
+       int result = register_read_abstract(target, NULL, GDB_REGNO_S0, 64);
+       if (result == ERROR_OK)
+               r->xlen = 64;
+       else
+               r->xlen = 32;
 
-               if (riscv_supports_extension(target, i, 'V')) {
-                       if (discover_vlenb(target, i) != ERROR_OK)
-                               return ERROR_FAIL;
-               }
+       if (register_read(target, &r->misa, GDB_REGNO_MISA)) {
+               LOG_ERROR("Fatal: Failed to read MISA from hart %d.", r->current_hartid);
+               return ERROR_FAIL;
+       }
 
-               /* Now init registers based on what we discovered. */
-               if (riscv_init_registers(target) != ERROR_OK)
+       if (riscv_supports_extension(target, 'V')) {
+               if (discover_vlenb(target) != ERROR_OK)
                        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]);
+       /* Display this as early as possible to help people who are using
+               * really slow simulators. */
+       LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, r->current_hartid, r->xlen,
+                       r->misa);
 
-               if (!halted)
-                       riscv013_step_or_resume_current_hart(target, false, false);
-       }
+       if (!halted)
+               riscv013_step_or_resume_current_hart(target, false, false);
 
        target_set_examined(target);
 
@@ -1780,27 +1778,31 @@ static int examine(struct target *target)
         * 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);
-               }
-       }
+       LOG_INFO(" hart %d: XLEN=%d, misa=0x%" PRIx64, r->current_hartid, r->xlen,
+                       r->misa);
        return ERROR_OK;
 }
 
-int riscv013_authdata_read(struct target *target, uint32_t *value)
+static int riscv013_authdata_read(struct target *target, uint32_t *value, unsigned int index)
 {
+       if (index > 0) {
+               LOG_ERROR("Spec 0.13 only has a single authdata register.");
+               return ERROR_FAIL;
+       }
+
        if (wait_for_authbusy(target, NULL) != ERROR_OK)
                return ERROR_FAIL;
 
        return dmi_read(target, value, DM_AUTHDATA);
 }
 
-int riscv013_authdata_write(struct target *target, uint32_t value)
+static int riscv013_authdata_write(struct target *target, uint32_t value, unsigned int index)
 {
+       if (index > 0) {
+               LOG_ERROR("Spec 0.13 only has a single authdata register.");
+               return ERROR_FAIL;
+       }
+
        uint32_t before, after;
        if (wait_for_authbusy(target, &before) != ERROR_OK)
                return ERROR_FAIL;
@@ -1835,27 +1837,74 @@ static int riscv013_hart_count(struct target *target)
        return dm->hart_count;
 }
 
+/* Try to find out the widest memory access size depending on the selected memory access methods. */
 static unsigned riscv013_data_bits(struct target *target)
 {
        RISCV013_INFO(info);
-       /* TODO: Once there is a spec for discovering abstract commands, we can
-        * take those into account as well.  For now we assume abstract commands
-        * support XLEN-wide accesses. */
-       if (has_sufficient_progbuf(target, 3) && !riscv_prefer_sba)
-               return riscv_xlen(target);
+       RISCV_INFO(r);
 
-       if (get_field(info->sbcs, DM_SBCS_SBACCESS128))
-               return 128;
-       if (get_field(info->sbcs, DM_SBCS_SBACCESS64))
-               return 64;
-       if (get_field(info->sbcs, DM_SBCS_SBACCESS32))
-               return 32;
-       if (get_field(info->sbcs, DM_SBCS_SBACCESS16))
-               return 16;
-       if (get_field(info->sbcs, DM_SBCS_SBACCESS8))
-               return 8;
-
-       return riscv_xlen(target);
+       for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) {
+               int method = r->mem_access_methods[i];
+
+               if (method == RISCV_MEM_ACCESS_PROGBUF) {
+                       if (has_sufficient_progbuf(target, 3))
+                               return riscv_xlen(target);
+               } else if (method == RISCV_MEM_ACCESS_SYSBUS) {
+                       if (get_field(info->sbcs, DM_SBCS_SBACCESS128))
+                               return 128;
+                       if (get_field(info->sbcs, DM_SBCS_SBACCESS64))
+                               return 64;
+                       if (get_field(info->sbcs, DM_SBCS_SBACCESS32))
+                               return 32;
+                       if (get_field(info->sbcs, DM_SBCS_SBACCESS16))
+                               return 16;
+                       if (get_field(info->sbcs, DM_SBCS_SBACCESS8))
+                               return 8;
+               } else if (method == RISCV_MEM_ACCESS_ABSTRACT) {
+                       /* TODO: Once there is a spec for discovering abstract commands, we can
+                        * take those into account as well.  For now we assume abstract commands
+                        * support XLEN-wide accesses. */
+                       return riscv_xlen(target);
+               } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED)
+                       /* No further mem access method to try. */
+                       break;
+       }
+       LOG_ERROR("Unable to determine supported data bits on this target. Assuming 32 bits.");
+       return 32;
+}
+
+COMMAND_HELPER(riscv013_print_info, struct target *target)
+{
+       RISCV013_INFO(info);
+
+       /* Abstract description. */
+       riscv_print_info_line(CMD, "target", "memory.read_while_running8", get_field(info->sbcs, DM_SBCS_SBACCESS8));
+       riscv_print_info_line(CMD, "target", "memory.write_while_running8", get_field(info->sbcs, DM_SBCS_SBACCESS8));
+       riscv_print_info_line(CMD, "target", "memory.read_while_running16", get_field(info->sbcs, DM_SBCS_SBACCESS16));
+       riscv_print_info_line(CMD, "target", "memory.write_while_running16", get_field(info->sbcs, DM_SBCS_SBACCESS16));
+       riscv_print_info_line(CMD, "target", "memory.read_while_running32", get_field(info->sbcs, DM_SBCS_SBACCESS32));
+       riscv_print_info_line(CMD, "target", "memory.write_while_running32", get_field(info->sbcs, DM_SBCS_SBACCESS32));
+       riscv_print_info_line(CMD, "target", "memory.read_while_running64", get_field(info->sbcs, DM_SBCS_SBACCESS64));
+       riscv_print_info_line(CMD, "target", "memory.write_while_running64", get_field(info->sbcs, DM_SBCS_SBACCESS64));
+       riscv_print_info_line(CMD, "target", "memory.read_while_running128", get_field(info->sbcs, DM_SBCS_SBACCESS128));
+       riscv_print_info_line(CMD, "target", "memory.write_while_running128", get_field(info->sbcs, DM_SBCS_SBACCESS128));
+
+       /* Lower level description. */
+       riscv_print_info_line(CMD, "dm", "abits", info->abits);
+       riscv_print_info_line(CMD, "dm", "progbufsize", info->progbufsize);
+       riscv_print_info_line(CMD, "dm", "sbversion", get_field(info->sbcs, DM_SBCS_SBVERSION));
+       riscv_print_info_line(CMD, "dm", "sbasize", get_field(info->sbcs, DM_SBCS_SBASIZE));
+       riscv_print_info_line(CMD, "dm", "sbaccess128", get_field(info->sbcs, DM_SBCS_SBACCESS128));
+       riscv_print_info_line(CMD, "dm", "sbaccess64", get_field(info->sbcs, DM_SBCS_SBACCESS64));
+       riscv_print_info_line(CMD, "dm", "sbaccess32", get_field(info->sbcs, DM_SBCS_SBACCESS32));
+       riscv_print_info_line(CMD, "dm", "sbaccess16", get_field(info->sbcs, DM_SBCS_SBACCESS16));
+       riscv_print_info_line(CMD, "dm", "sbaccess8", get_field(info->sbcs, DM_SBCS_SBACCESS8));
+
+       uint32_t dmstatus;
+       if (dmstatus_read(target, &dmstatus, false) == ERROR_OK)
+               riscv_print_info_line(CMD, "dm", "authenticated", get_field(dmstatus, DM_DMSTATUS_AUTHENTICATED));
+
+       return 0;
 }
 
 static int prep_for_vector_access(struct target *target, uint64_t *vtype,
@@ -1885,8 +1934,7 @@ static int prep_for_vector_access(struct target *target, uint64_t *vtype,
 
        if (register_write_direct(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK)
                return ERROR_FAIL;
-       *debug_vl = DIV_ROUND_UP(r->vlenb[r->current_hartid] * 8,
-                       riscv_xlen(target));
+       *debug_vl = DIV_ROUND_UP(r->vlenb * 8, riscv_xlen(target));
        if (register_write_direct(target, GDB_REGNO_VL, *debug_vl) != ERROR_OK)
                return ERROR_FAIL;
 
@@ -1909,6 +1957,9 @@ static int riscv013_get_register_buf(struct target *target,
 {
        assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
 
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+
        riscv_reg_t s0;
        if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
                return ERROR_FAIL;
@@ -1965,6 +2016,9 @@ static int riscv013_set_register_buf(struct target *target,
 {
        assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
 
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+
        riscv_reg_t s0;
        if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
                return ERROR_FAIL;
@@ -2005,11 +2059,227 @@ static int riscv013_set_register_buf(struct target *target,
        return result;
 }
 
+static uint32_t sb_sbaccess(unsigned int size_bytes)
+{
+       switch (size_bytes) {
+               case 1:
+                       return set_field(0, DM_SBCS_SBACCESS, 0);
+               case 2:
+                       return set_field(0, DM_SBCS_SBACCESS, 1);
+               case 4:
+                       return set_field(0, DM_SBCS_SBACCESS, 2);
+               case 8:
+                       return set_field(0, DM_SBCS_SBACCESS, 3);
+               case 16:
+                       return set_field(0, DM_SBCS_SBACCESS, 4);
+       }
+       assert(0);
+       return 0;
+}
+
+static int sb_write_address(struct target *target, target_addr_t address,
+                                                       bool ensure_success)
+{
+       RISCV013_INFO(info);
+       unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE);
+       /* There currently is no support for >64-bit addresses in OpenOCD. */
+       if (sbasize > 96)
+               dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS3, 0, false, false);
+       if (sbasize > 64)
+               dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS2, 0, false, false);
+       if (sbasize > 32)
+               dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS1, address >> 32, false, false);
+       return dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS0, address,
+                                 false, ensure_success);
+}
+
+static int batch_run(const struct target *target, struct riscv_batch *batch)
+{
+       RISCV013_INFO(info);
+       RISCV_INFO(r);
+       if (r->reset_delays_wait >= 0) {
+               r->reset_delays_wait -= batch->used_scans;
+               if (r->reset_delays_wait <= 0) {
+                       batch->idle_count = 0;
+                       info->dmi_busy_delay = 0;
+                       info->ac_busy_delay = 0;
+               }
+       }
+       return riscv_batch_run(batch);
+}
+
+static int sba_supports_access(struct target *target, unsigned int size_bytes)
+{
+       RISCV013_INFO(info);
+       switch (size_bytes) {
+               case 1:
+                       return get_field(info->sbcs, DM_SBCS_SBACCESS8);
+               case 2:
+                       return get_field(info->sbcs, DM_SBCS_SBACCESS16);
+               case 4:
+                       return get_field(info->sbcs, DM_SBCS_SBACCESS32);
+               case 8:
+                       return get_field(info->sbcs, DM_SBCS_SBACCESS64);
+               case 16:
+                       return get_field(info->sbcs, DM_SBCS_SBACCESS128);
+               default:
+                       return 0;
+       }
+}
+
+static int sample_memory_bus_v1(struct target *target,
+                                                               struct riscv_sample_buf *buf,
+                                                               const riscv_sample_config_t *config,
+                                                               int64_t until_ms)
+{
+       RISCV013_INFO(info);
+       unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE);
+       if (sbasize > 64) {
+               LOG_ERROR("Memory sampling is only implemented for sbasize <= 64.");
+               return ERROR_NOT_IMPLEMENTED;
+       }
+
+       if (get_field(info->sbcs, DM_SBCS_SBVERSION) != 1) {
+               LOG_ERROR("Memory sampling is only implemented for SBA version 1.");
+               return ERROR_NOT_IMPLEMENTED;
+       }
+
+       uint32_t sbcs = 0;
+       uint32_t sbcs_valid = false;
+
+       uint32_t sbaddress0 = 0;
+       bool sbaddress0_valid = false;
+       uint32_t sbaddress1 = 0;
+       bool sbaddress1_valid = false;
+
+       /* How often to read each value in a batch. */
+       const unsigned int repeat = 5;
+
+       unsigned int enabled_count = 0;
+       for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) {
+               if (config->bucket[i].enabled)
+                       enabled_count++;
+       }
+
+       while (timeval_ms() < until_ms) {
+               /*
+                * batch_run() adds to the batch, so we can't simply reuse the same
+                * batch over and over. So we create a new one every time through the
+                * loop.
+                */
+               struct riscv_batch *batch = riscv_batch_alloc(
+                       target, 1 + enabled_count * 5 * repeat,
+                       info->dmi_busy_delay + info->bus_master_read_delay);
+               if (!batch)
+                       return ERROR_FAIL;
+
+               unsigned int result_bytes = 0;
+               for (unsigned int n = 0; n < repeat; n++) {
+                       for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) {
+                               if (config->bucket[i].enabled) {
+                                       if (!sba_supports_access(target, config->bucket[i].size_bytes)) {
+                                               LOG_ERROR("Hardware does not support SBA access for %d-byte memory sampling.",
+                                                               config->bucket[i].size_bytes);
+                                               return ERROR_NOT_IMPLEMENTED;
+                                       }
+
+                                       uint32_t sbcs_write = DM_SBCS_SBREADONADDR;
+                                       if (enabled_count == 1)
+                                               sbcs_write |= DM_SBCS_SBREADONDATA;
+                                       sbcs_write |= sb_sbaccess(config->bucket[i].size_bytes);
+                                       if (!sbcs_valid || sbcs_write != sbcs) {
+                                               riscv_batch_add_dmi_write(batch, DM_SBCS, sbcs_write);
+                                               sbcs = sbcs_write;
+                                               sbcs_valid = true;
+                                       }
+
+                                       if (sbasize > 32 &&
+                                                       (!sbaddress1_valid ||
+                                                       sbaddress1 != config->bucket[i].address >> 32)) {
+                                               sbaddress1 = config->bucket[i].address >> 32;
+                                               riscv_batch_add_dmi_write(batch, DM_SBADDRESS1, sbaddress1);
+                                               sbaddress1_valid = true;
+                                       }
+                                       if (!sbaddress0_valid ||
+                                                       sbaddress0 != (config->bucket[i].address & 0xffffffff)) {
+                                               sbaddress0 = config->bucket[i].address;
+                                               riscv_batch_add_dmi_write(batch, DM_SBADDRESS0, sbaddress0);
+                                               sbaddress0_valid = true;
+                                       }
+                                       if (config->bucket[i].size_bytes > 4)
+                                               riscv_batch_add_dmi_read(batch, DM_SBDATA1);
+                                       riscv_batch_add_dmi_read(batch, DM_SBDATA0);
+                                       result_bytes += 1 + config->bucket[i].size_bytes;
+                               }
+                       }
+               }
+
+               if (buf->used + result_bytes >= buf->size) {
+                       riscv_batch_free(batch);
+                       break;
+               }
+
+               size_t sbcs_key = riscv_batch_add_dmi_read(batch, DM_SBCS);
+
+               int result = batch_run(target, batch);
+               if (result != ERROR_OK)
+                       return result;
+
+               uint32_t sbcs_read = riscv_batch_get_dmi_read_data(batch, sbcs_key);
+               if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) {
+                       /* Discard this batch (too much hassle to try to recover partial
+                        * data) and try again with a larger delay. */
+                       info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1;
+                       dmi_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR);
+                       riscv_batch_free(batch);
+                       continue;
+               }
+               if (get_field(sbcs_read, DM_SBCS_SBERROR)) {
+                       /* The memory we're sampling was unreadable, somehow. Give up. */
+                       dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR);
+                       riscv_batch_free(batch);
+                       return ERROR_FAIL;
+               }
+
+               unsigned int read = 0;
+               for (unsigned int n = 0; n < repeat; n++) {
+                       for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) {
+                               if (config->bucket[i].enabled) {
+                                       assert(i < RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE);
+                                       uint64_t value = 0;
+                                       if (config->bucket[i].size_bytes > 4)
+                                               value = ((uint64_t)riscv_batch_get_dmi_read_data(batch, read++)) << 32;
+                                       value |= riscv_batch_get_dmi_read_data(batch, read++);
+
+                                       buf->buf[buf->used] = i;
+                                       buf_set_u64(buf->buf + buf->used + 1, 0, config->bucket[i].size_bytes * 8, value);
+                                       buf->used += 1 + config->bucket[i].size_bytes;
+                               }
+                       }
+               }
+
+               riscv_batch_free(batch);
+       }
+
+       return ERROR_OK;
+}
+
+static int sample_memory(struct target *target,
+                                                struct riscv_sample_buf *buf,
+                                                riscv_sample_config_t *config,
+                                                int64_t until_ms)
+{
+       if (!config->enabled)
+               return ERROR_OK;
+
+       return sample_memory_bus_v1(target, buf, config, until_ms);
+}
+
 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;
+       RISCV_INFO(generic_info);
 
        generic_info->get_register = &riscv013_get_register;
        generic_info->set_register = &riscv013_set_register;
@@ -2038,12 +2308,13 @@ static int init_target(struct command_context *cmd_ctx,
        generic_info->dmi_write = &dmi_write;
        generic_info->read_memory = read_memory;
        generic_info->test_sba_config_reg = &riscv013_test_sba_config_reg;
-       generic_info->test_compliance = &riscv013_test_compliance;
        generic_info->hart_count = &riscv013_hart_count;
        generic_info->data_bits = &riscv013_data_bits;
+       generic_info->print_info = &riscv013_print_info;
        generic_info->version_specific = calloc(1, sizeof(riscv013_info_t));
        if (!generic_info->version_specific)
                return ERROR_FAIL;
+       generic_info->sample_memory = sample_memory;
        riscv013_info_t *info = get_info(target);
 
        info->progbufsize = -1;
@@ -2063,6 +2334,8 @@ static int init_target(struct command_context *cmd_ctx,
        info->abstract_read_fpr_supported = true;
        info->abstract_write_fpr_supported = true;
 
+       info->has_aampostincrement = YNM_MAYBE;
+
        return ERROR_OK;
 }
 
@@ -2074,7 +2347,10 @@ static int assert_reset(struct target *target)
 
        uint32_t control_base = set_field(0, DM_DMCONTROL_DMACTIVE, 1);
 
-       if (target->rtos) {
+       if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) {
+               /* Run the user-supplied script if there is one. */
+               target_handle_event(target, TARGET_EVENT_RESET_ASSERT);
+       } else if (target->rtos) {
                /* There's only one target, and OpenOCD thinks each hart is a thread.
                 * We must reset them all. */
 
@@ -2082,15 +2358,12 @@ static int assert_reset(struct target *target)
 
                /* 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, DM_DMCONTROL_HALTREQ,
-                                       target->reset_halt ? 1 : 0);
-                       dmi_write(target, DM_DMCONTROL, control);
-               }
+               control = set_hartsel(control_base, target->coreid);
+               control = set_field(control, DM_DMCONTROL_HALTREQ,
+                               target->reset_halt ? 1 : 0);
+               dmi_write(target, DM_DMCONTROL, control);
+
                /* Assert ndmreset */
                control = set_field(control, DM_DMCONTROL_NDMRESET, 1);
                dmi_write(target, DM_DMCONTROL, control);
@@ -2138,7 +2411,7 @@ static int deassert_reset(struct target *target)
        for (int i = 0; i < riscv_count_harts(target); ++i) {
                int index = i;
                if (target->rtos) {
-                       if (!riscv_hart_enabled(target, index))
+                       if (index != target->coreid)
                                continue;
                        dmi_write(target, DM_DMCONTROL,
                                        set_hartsel(control, index));
@@ -2146,16 +2419,7 @@ static int deassert_reset(struct target *target)
                        index = r->current_hartid;
                }
 
-               char *operation;
-               uint32_t expected_field;
-               if (target->reset_halt) {
-                       operation = "halt";
-                       expected_field = DM_DMSTATUS_ALLHALTED;
-               } else {
-                       operation = "run";
-                       expected_field = DM_DMSTATUS_ALLRUNNING;
-               }
-               LOG_DEBUG("Waiting for hart %d to %s out of reset.", index, operation);
+               LOG_DEBUG("Waiting for hart %d to come out of reset.", index);
                while (1) {
                        int result = dmstatus_read_timeout(target, &dmstatus, true,
                                        riscv_reset_timeout_sec);
@@ -2166,13 +2430,20 @@ static int deassert_reset(struct target *target)
                                                index, riscv_reset_timeout_sec);
                        if (result != ERROR_OK)
                                return result;
-                       if (get_field(dmstatus, expected_field))
+                       /* Certain debug modules, like the one in GD32VF103
+                        * MCUs, violate the specification's requirement that
+                        * each hart is in "exactly one of four states" and,
+                        * during reset, report harts as both unavailable and
+                        * halted/running. To work around this, we check for
+                        * the absence of the unavailable state rather than
+                        * the presence of any other state. */
+                       if (!get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL))
                                break;
                        if (time(NULL) - start > riscv_reset_timeout_sec) {
-                               LOG_ERROR("Hart %d didn't %s coming out of reset in %ds; "
+                               LOG_ERROR("Hart %d didn't leave reset in %ds; "
                                                "dmstatus=0x%x; "
                                                "Increase the timeout with riscv set_reset_timeout_sec.",
-                                               index, operation, riscv_reset_timeout_sec, dmstatus);
+                                               index, riscv_reset_timeout_sec, dmstatus);
                                return ERROR_FAIL;
                        }
                }
@@ -2194,8 +2465,6 @@ static int deassert_reset(struct target *target)
 
 static int execute_fence(struct target *target)
 {
-       int old_hartid = riscv_current_hartid(target);
-
        /* FIXME: For non-coherent systems we need to flush the caches right
         * here, but there's no ISA-defined way of doing that. */
        {
@@ -2208,27 +2477,6 @@ static int execute_fence(struct target *target)
                        LOG_DEBUG("Unable to execute pre-fence");
        }
 
-       for (int i = 0; i < riscv_count_harts(target); ++i) {
-               if (!riscv_hart_enabled(target, i))
-                       continue;
-
-               if (i == old_hartid)
-                       /* Fence already executed for this hart */
-                       continue;
-
-               riscv_set_current_hartid(target, i);
-
-               struct riscv_program program;
-               riscv_program_init(&program, target);
-               riscv_program_fence_i(&program);
-               riscv_program_fence(&program);
-               int result = riscv_program_exec(&program, target);
-               if (result != ERROR_OK)
-                       LOG_DEBUG("Unable to execute fence on hart %d", i);
-       }
-
-       riscv_set_current_hartid(target, old_hartid);
-
        return ERROR_OK;
 }
 
@@ -2278,24 +2526,6 @@ static int read_memory_bus_word(struct target *target, target_addr_t address,
        return ERROR_OK;
 }
 
-static uint32_t sb_sbaccess(unsigned size_bytes)
-{
-       switch (size_bytes) {
-               case 1:
-                       return set_field(0, DM_SBCS_SBACCESS, 0);
-               case 2:
-                       return set_field(0, DM_SBCS_SBACCESS, 1);
-               case 4:
-                       return set_field(0, DM_SBCS_SBACCESS, 2);
-               case 8:
-                       return set_field(0, DM_SBCS_SBACCESS, 3);
-               case 16:
-                       return set_field(0, DM_SBCS_SBACCESS, 4);
-       }
-       assert(0);
-       return 0;       /* Make mingw happy. */
-}
-
 static target_addr_t sb_read_address(struct target *target)
 {
        RISCV013_INFO(info);
@@ -2312,20 +2542,6 @@ static target_addr_t sb_read_address(struct target *target)
        return address;
 }
 
-static int sb_write_address(struct target *target, target_addr_t address)
-{
-       RISCV013_INFO(info);
-       unsigned sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE);
-       /* There currently is no support for >64-bit addresses in OpenOCD. */
-       if (sbasize > 96)
-               dmi_write(target, DM_SBADDRESS3, 0);
-       if (sbasize > 64)
-               dmi_write(target, DM_SBADDRESS2, 0);
-       if (sbasize > 32)
-               dmi_write(target, DM_SBADDRESS1, address >> 32);
-       return dmi_write(target, DM_SBADDRESS0, address);
-}
-
 static int read_sbcs_nonbusy(struct target *target, uint32_t *sbcs)
 {
        time_t start = time(NULL);
@@ -2453,6 +2669,10 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address,
                }
        }
 
+       uint32_t sbcs;
+       if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK)
+               return ERROR_FAIL;
+
        return ERROR_OK;
 }
 
@@ -2482,7 +2702,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
                        return ERROR_FAIL;
 
                /* This address write will trigger the first read. */
-               if (sb_write_address(target, next_address) != ERROR_OK)
+               if (sb_write_address(target, next_address, true) != ERROR_OK)
                        return ERROR_FAIL;
 
                if (info->bus_master_read_delay) {
@@ -2509,6 +2729,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
                                                                  next_read);
                                                return ERROR_FAIL;
                                        }
+                                       keep_alive();
                                        dmi_status_t status = dmi_scan(target, NULL, &value,
                                                                                                   DMI_OP_READ, sbdata[j], 0, false);
                                        if (status == DMI_STATUS_BUSY)
@@ -2570,7 +2791,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
 
                if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) {
                        /* We read while the target was busy. Slow down and try again. */
-                       if (dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR) != ERROR_OK)
+                       if (dmi_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR) != ERROR_OK)
                                return ERROR_FAIL;
                        next_address = sb_read_address(target);
                        info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1;
@@ -2592,19 +2813,136 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
        return ERROR_OK;
 }
 
-static int batch_run(const struct target *target, struct riscv_batch *batch)
+static void log_mem_access_result(struct target *target, bool success, int method, bool read)
 {
-       RISCV013_INFO(info);
        RISCV_INFO(r);
-       if (r->reset_delays_wait >= 0) {
-               r->reset_delays_wait -= batch->used_scans;
-               if (r->reset_delays_wait <= 0) {
-                       batch->idle_count = 0;
-                       info->dmi_busy_delay = 0;
-                       info->ac_busy_delay = 0;
+       bool warn = false;
+       char msg[60];
+
+       /* Compose the message */
+       snprintf(msg, 60, "%s to %s memory via %s.",
+                       success ? "Succeeded" : "Failed",
+                       read ? "read" : "write",
+                       (method == RISCV_MEM_ACCESS_PROGBUF) ? "program buffer" :
+                       (method == RISCV_MEM_ACCESS_SYSBUS) ? "system bus" : "abstract access");
+
+       /* Determine the log message severity. Show warnings only once. */
+       if (!success) {
+               if (method == RISCV_MEM_ACCESS_PROGBUF) {
+                       warn = r->mem_access_progbuf_warn;
+                       r->mem_access_progbuf_warn = false;
+               }
+               if (method == RISCV_MEM_ACCESS_SYSBUS) {
+                       warn = r->mem_access_sysbus_warn;
+                       r->mem_access_sysbus_warn = false;
+               }
+               if (method == RISCV_MEM_ACCESS_ABSTRACT) {
+                       warn = r->mem_access_abstract_warn;
+                       r->mem_access_abstract_warn = false;
                }
        }
-       return riscv_batch_run(batch);
+
+       if (warn)
+               LOG_WARNING("%s", msg);
+       else
+               LOG_DEBUG("%s", msg);
+}
+
+static bool mem_should_skip_progbuf(struct target *target, target_addr_t address,
+               uint32_t size, bool read, char **skip_reason)
+{
+       assert(skip_reason);
+
+       if (!has_sufficient_progbuf(target, 3)) {
+               LOG_DEBUG("Skipping mem %s via progbuf - insufficient progbuf size.",
+                               read ? "read" : "write");
+               *skip_reason = "skipped (insufficient progbuf)";
+               return true;
+       }
+       if (target->state != TARGET_HALTED) {
+               LOG_DEBUG("Skipping mem %s via progbuf - target not halted.",
+                               read ? "read" : "write");
+               *skip_reason = "skipped (target not halted)";
+               return true;
+       }
+       if (riscv_xlen(target) < size * 8) {
+               LOG_DEBUG("Skipping mem %s via progbuf - XLEN (%d) is too short for %d-bit memory access.",
+                               read ? "read" : "write", riscv_xlen(target), size * 8);
+               *skip_reason = "skipped (XLEN too short)";
+               return true;
+       }
+       if (size > 8) {
+               LOG_DEBUG("Skipping mem %s via progbuf - unsupported size.",
+                               read ? "read" : "write");
+               *skip_reason = "skipped (unsupported size)";
+               return true;
+       }
+       if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) {
+               LOG_DEBUG("Skipping mem %s via progbuf - progbuf only supports %u-bit address.",
+                               read ? "read" : "write", riscv_xlen(target));
+               *skip_reason = "skipped (too large address)";
+               return true;
+       }
+
+       return false;
+}
+
+static bool mem_should_skip_sysbus(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t increment, bool read, char **skip_reason)
+{
+       assert(skip_reason);
+
+       RISCV013_INFO(info);
+       if (!sba_supports_access(target, size)) {
+               LOG_DEBUG("Skipping mem %s via system bus - unsupported size.",
+                               read ? "read" : "write");
+               *skip_reason = "skipped (unsupported size)";
+               return true;
+       }
+       unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE);
+       if ((sizeof(address) * 8 > sbasize) && (address >> sbasize)) {
+               LOG_DEBUG("Skipping mem %s via system bus - sba only supports %u-bit address.",
+                               read ? "read" : "write", sbasize);
+               *skip_reason = "skipped (too large address)";
+               return true;
+       }
+       if (read && increment != size && (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0 || increment != 0)) {
+               LOG_DEBUG("Skipping mem read via system bus - "
+                               "sba reads only support size==increment or also size==0 for sba v1.");
+               *skip_reason = "skipped (unsupported increment)";
+               return true;
+       }
+
+       return false;
+}
+
+static bool mem_should_skip_abstract(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t increment, bool read, char **skip_reason)
+{
+       assert(skip_reason);
+
+       if (size > 8) {
+               /* TODO: Add 128b support if it's ever used. Involves modifying
+                                read/write_abstract_arg() to work on two 64b values. */
+               LOG_DEBUG("Skipping mem %s via abstract access - unsupported size: %d bits",
+                               read ? "read" : "write", size * 8);
+               *skip_reason = "skipped (unsupported size)";
+               return true;
+       }
+       if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) {
+               LOG_DEBUG("Skipping mem %s via abstract access - abstract access only supports %u-bit address.",
+                               read ? "read" : "write", riscv_xlen(target));
+               *skip_reason = "skipped (too large address)";
+               return true;
+       }
+       if (read && size != increment) {
+               LOG_ERROR("Skipping mem read via abstract access - "
+                               "abstract command reads only support size==increment.");
+               *skip_reason = "skipped (unsupported increment)";
+               return true;
+       }
+
+       return false;
 }
 
 /*
@@ -2615,12 +2953,10 @@ static int batch_run(const struct target *target, struct riscv_batch *batch)
 static int read_memory_abstract(struct target *target, target_addr_t address,
                uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
 {
-       if (size != increment) {
-               LOG_ERROR("abstract command reads only support size==increment");
-               return ERROR_NOT_IMPLEMENTED;
-       }
+       RISCV013_INFO(info);
 
        int result = ERROR_OK;
+       bool use_aampostincrement = info->has_aampostincrement != YNM_NO;
 
        LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
                          size, address);
@@ -2629,25 +2965,19 @@ static int read_memory_abstract(struct target *target, target_addr_t address,
 
        /* Convert the size (bytes) to width (bits) */
        unsigned width = size << 3;
-       if (width > 64) {
-               /* TODO: Add 128b support if it's ever used. Involves modifying
-                                read/write_abstract_arg() to work on two 64b values. */
-               LOG_ERROR("Unsupported size: %d bits", size);
-               return ERROR_FAIL;
-       }
 
        /* Create the command (physical address, postincrement, read) */
-       uint32_t command = access_memory_command(target, false, width, true, false);
+       uint32_t command = access_memory_command(target, false, width, use_aampostincrement, false);
 
        /* Execute the reads */
        uint8_t *p = buffer;
        bool updateaddr = true;
-       unsigned width32 = (width + 31) / 32 * 32;
+       unsigned int width32 = (width < 32) ? 32 : width;
        for (uint32_t c = 0; c < count; c++) {
-               /* Only update the address initially and let postincrement update it */
+               /* Update the address if it is the first time or aampostincrement is not supported by the target. */
                if (updateaddr) {
                        /* Set arg1 to the address: address + c * size */
-                       result = write_abstract_arg(target, 1, address, riscv_xlen(target));
+                       result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target));
                        if (result != ERROR_OK) {
                                LOG_ERROR("Failed to write arg1 during read_memory_abstract().");
                                return result;
@@ -2656,16 +2986,38 @@ static int read_memory_abstract(struct target *target, target_addr_t address,
 
                /* Execute the command */
                result = execute_abstract_command(target, command);
-               if (result != ERROR_OK) {
-                       LOG_ERROR("Failed to execute command read_memory_abstract().");
-                       return result;
+
+               if (info->has_aampostincrement == YNM_MAYBE) {
+                       if (result == ERROR_OK) {
+                               /* Safety: double-check that the address was really auto-incremented */
+                               riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target));
+                               if (new_address == address + size) {
+                                       LOG_DEBUG("aampostincrement is supported on this target.");
+                                       info->has_aampostincrement = YNM_YES;
+                               } else {
+                                       LOG_WARNING("Buggy aampostincrement! Address not incremented correctly.");
+                                       info->has_aampostincrement = YNM_NO;
+                               }
+                       } else {
+                               /* Try the same access but with postincrement disabled. */
+                               command = access_memory_command(target, false, width, false, false);
+                               result = execute_abstract_command(target, command);
+                               if (result == ERROR_OK) {
+                                       LOG_DEBUG("aampostincrement is not supported on this target.");
+                                       info->has_aampostincrement = YNM_NO;
+                               }
+                       }
                }
 
+               if (result != ERROR_OK)
+                       return result;
+
                /* Copy arg0 to buffer (rounded width up to nearest 32) */
                riscv_reg_t value = read_abstract_arg(target, 0, width32);
                buf_set_u64(p, 0, 8 * size, value);
 
-               updateaddr = false;
+               if (info->has_aampostincrement == YNM_YES)
+                       updateaddr = false;
                p += size;
        }
 
@@ -2680,22 +3032,18 @@ static int read_memory_abstract(struct target *target, target_addr_t address,
 static int write_memory_abstract(struct target *target, target_addr_t address,
                uint32_t size, uint32_t count, const uint8_t *buffer)
 {
+       RISCV013_INFO(info);
        int result = ERROR_OK;
+       bool use_aampostincrement = info->has_aampostincrement != YNM_NO;
 
        LOG_DEBUG("writing %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
                          size, address);
 
        /* Convert the size (bytes) to width (bits) */
        unsigned width = size << 3;
-       if (width > 64) {
-               /* TODO: Add 128b support if it's ever used. Involves modifying
-                                read/write_abstract_arg() to work on two 64b values. */
-               LOG_ERROR("Unsupported size: %d bits", width);
-               return ERROR_FAIL;
-       }
 
        /* Create the command (physical address, postincrement, write) */
-       uint32_t command = access_memory_command(target, false, width, true, true);
+       uint32_t command = access_memory_command(target, false, width, use_aampostincrement, true);
 
        /* Execute the writes */
        const uint8_t *p = buffer;
@@ -2709,10 +3057,10 @@ static int write_memory_abstract(struct target *target, target_addr_t address,
                        return result;
                }
 
-               /* Only update the address initially and let postincrement update it */
+               /* Update the address if it is the first time or aampostincrement is not supported by the target. */
                if (updateaddr) {
                        /* Set arg1 to the address: address + c * size */
-                       result = write_abstract_arg(target, 1, address, riscv_xlen(target));
+                       result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target));
                        if (result != ERROR_OK) {
                                LOG_ERROR("Failed to write arg1 during write_memory_abstract().");
                                return result;
@@ -2721,12 +3069,34 @@ static int write_memory_abstract(struct target *target, target_addr_t address,
 
                /* Execute the command */
                result = execute_abstract_command(target, command);
-               if (result != ERROR_OK) {
-                       LOG_ERROR("Failed to execute command write_memory_abstract().");
-                       return result;
+
+               if (info->has_aampostincrement == YNM_MAYBE) {
+                       if (result == ERROR_OK) {
+                               /* Safety: double-check that the address was really auto-incremented */
+                               riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target));
+                               if (new_address == address + size) {
+                                       LOG_DEBUG("aampostincrement is supported on this target.");
+                                       info->has_aampostincrement = YNM_YES;
+                               } else {
+                                       LOG_WARNING("Buggy aampostincrement! Address not incremented correctly.");
+                                       info->has_aampostincrement = YNM_NO;
+                               }
+                       } else {
+                               /* Try the same access but with postincrement disabled. */
+                               command = access_memory_command(target, false, width, false, true);
+                               result = execute_abstract_command(target, command);
+                               if (result == ERROR_OK) {
+                                       LOG_DEBUG("aampostincrement is not supported on this target.");
+                                       info->has_aampostincrement = YNM_NO;
+                               }
+                       }
                }
 
-               updateaddr = false;
+               if (result != ERROR_OK)
+                       return result;
+
+               if (info->has_aampostincrement == YNM_YES)
+                       updateaddr = false;
                p += size;
        }
 
@@ -2982,9 +3352,10 @@ static int read_memory_progbuf_one(struct target *target, target_addr_t address,
                return ERROR_FAIL;
 
        uint64_t s0;
+       int result = ERROR_FAIL;
 
        if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
-               return ERROR_FAIL;
+               goto restore_mstatus;
 
        /* Write the program (load, increment) */
        struct riscv_program program;
@@ -3006,40 +3377,42 @@ static int read_memory_progbuf_one(struct target *target, target_addr_t address,
                        break;
                default:
                        LOG_ERROR("Unsupported size: %d", size);
-                       return ERROR_FAIL;
+                       goto restore_mstatus;
        }
        if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV))
                riscv_program_csrrci(&program, GDB_REGNO_ZERO,  CSR_DCSR_MPRVEN, GDB_REGNO_DCSR);
 
        if (riscv_program_ebreak(&program) != ERROR_OK)
-               return ERROR_FAIL;
+               goto restore_mstatus;
        if (riscv_program_write(&program) != ERROR_OK)
-               return ERROR_FAIL;
+               goto restore_mstatus;
 
        /* Write address to S0, and execute buffer. */
        if (write_abstract_arg(target, 0, address, riscv_xlen(target)) != ERROR_OK)
-               return ERROR_FAIL;
+               goto restore_mstatus;
        uint32_t command = access_register_command(target, GDB_REGNO_S0,
                        riscv_xlen(target), AC_ACCESS_REGISTER_WRITE |
                        AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC);
        if (execute_abstract_command(target, command) != ERROR_OK)
-               return ERROR_FAIL;
+               goto restore_s0;
 
        uint64_t value;
        if (register_read(target, &value, GDB_REGNO_S0) != ERROR_OK)
-               return ERROR_FAIL;
+               goto restore_s0;
        buf_set_u64(buffer, 0, 8 * size, value);
        log_memory_access(address, value, size, true);
+       result = ERROR_OK;
 
+restore_s0:
        if (riscv_set_register(target, GDB_REGNO_S0, s0) != ERROR_OK)
-               return ERROR_FAIL;
+               result = ERROR_FAIL;
 
-       /* Restore MSTATUS */
+restore_mstatus:
        if (mstatus != mstatus_old)
                if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old))
-                       return ERROR_FAIL;
+                       result = ERROR_FAIL;
 
-       return ERROR_OK;
+       return result;
 }
 
 /**
@@ -3074,8 +3447,8 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
        if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK)
                return ERROR_FAIL;
 
-       /* s0 holds the next address to write to
-        * s1 holds the next data value to write
+       /* s0 holds the next address to read from
+        * s1 holds the next data value read
         * s2 is a counter in case increment is 0
         */
        uint64_t s0, s1, s2;
@@ -3083,7 +3456,7 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
                return ERROR_FAIL;
        if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK)
                return ERROR_FAIL;
-       if (increment == 0 && register_read(target, &s2, GDB_REGNO_S1) != ERROR_OK)
+       if (increment == 0 && register_read(target, &s2, GDB_REGNO_S2) != ERROR_OK)
                return ERROR_FAIL;
 
        /* Write the program (load, increment) */
@@ -3133,7 +3506,6 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
                uint8_t *buffer_i = buffer;
 
                for (uint32_t i = 0; i < count; i++, address_i += increment, buffer_i += size) {
-                       keep_alive();
                        /* TODO: This is much slower than it needs to be because we end up
                         * writing the address to read for every word we read. */
                        result = read_memory_progbuf_inner(target, address_i, size, count_i, buffer_i, increment);
@@ -3168,30 +3540,62 @@ static int read_memory(struct target *target, target_addr_t address,
        if (count == 0)
                return ERROR_OK;
 
-       RISCV013_INFO(info);
-       if (has_sufficient_progbuf(target, 3) && !riscv_prefer_sba)
-               return read_memory_progbuf(target, address, size, count, buffer,
-                       increment);
-
-       if ((get_field(info->sbcs, DM_SBCS_SBACCESS8) && size == 1) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS16) && size == 2) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS32) && size == 4) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS64) && size == 8) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS128) && size == 16)) {
-               if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0)
-                       return read_memory_bus_v0(target, address, size, count, buffer,
-                               increment);
-               else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1)
-                       return read_memory_bus_v1(target, address, size, count, buffer,
-                               increment);
+       if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) {
+               LOG_ERROR("BUG: Unsupported size for memory read: %d", size);
+               return ERROR_FAIL;
        }
 
-       if (has_sufficient_progbuf(target, 3))
-               return read_memory_progbuf(target, address, size, count, buffer,
-                       increment);
+       int ret = ERROR_FAIL;
+       RISCV_INFO(r);
+       RISCV013_INFO(info);
+
+       char *progbuf_result = "disabled";
+       char *sysbus_result = "disabled";
+       char *abstract_result = "disabled";
+
+       for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) {
+               int method = r->mem_access_methods[i];
+
+               if (method == RISCV_MEM_ACCESS_PROGBUF) {
+                       if (mem_should_skip_progbuf(target, address, size, true, &progbuf_result))
+                               continue;
+
+                       ret = read_memory_progbuf(target, address, size, count, buffer, increment);
+
+                       if (ret != ERROR_OK)
+                               progbuf_result = "failed";
+               } else if (method == RISCV_MEM_ACCESS_SYSBUS) {
+                       if (mem_should_skip_sysbus(target, address, size, increment, true, &sysbus_result))
+                               continue;
+
+                       if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0)
+                               ret = read_memory_bus_v0(target, address, size, count, buffer, increment);
+                       else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1)
+                               ret = read_memory_bus_v1(target, address, size, count, buffer, increment);
 
-       return read_memory_abstract(target, address, size, count, buffer,
-               increment);
+                       if (ret != ERROR_OK)
+                               sysbus_result = "failed";
+               } else if (method == RISCV_MEM_ACCESS_ABSTRACT) {
+                       if (mem_should_skip_abstract(target, address, size, increment, true, &abstract_result))
+                               continue;
+
+                       ret = read_memory_abstract(target, address, size, count, buffer, increment);
+
+                       if (ret != ERROR_OK)
+                               abstract_result = "failed";
+               } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED)
+                       /* No further mem access method to try. */
+                       break;
+
+               log_mem_access_result(target, ret == ERROR_OK, method, true);
+
+               if (ret == ERROR_OK)
+                       return ret;
+       }
+
+       LOG_ERROR("Target %s: Failed to read memory (addr=0x%" PRIx64 ")", target_name(target), address);
+       LOG_ERROR("  progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result);
+       return ret;
 }
 
 static int write_memory_bus_v0(struct target *target, target_addr_t address,
@@ -3260,7 +3664,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
 
        int result;
 
-       sb_write_address(target, next_address);
+       sb_write_address(target, next_address, true);
        while (next_address < end_address) {
                LOG_DEBUG("transferring burst starting at address 0x%" TARGET_PRIxADDR,
                                next_address);
@@ -3310,54 +3714,76 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
                        next_address += size;
                }
 
+               /* Execute the batch of writes */
                result = batch_run(target, batch);
                riscv_batch_free(batch);
                if (result != ERROR_OK)
                        return result;
 
+               /* Read sbcs value.
+                * At the same time, detect if DMI busy has occurred during the batch write. */
                bool dmi_busy_encountered;
                if (dmi_op(target, &sbcs, &dmi_busy_encountered, DMI_OP_READ,
-                               DM_SBCS, 0, false, false) != ERROR_OK)
+                               DM_SBCS, 0, false, true) != ERROR_OK)
                        return ERROR_FAIL;
+               if (dmi_busy_encountered)
+                       LOG_DEBUG("DMI busy encountered during system bus write.");
 
+               /* Wait until sbbusy goes low */
                time_t start = time(NULL);
-               bool dmi_busy = dmi_busy_encountered;
-               while (get_field(sbcs, DM_SBCS_SBBUSY) || dmi_busy) {
+               while (get_field(sbcs, DM_SBCS_SBBUSY)) {
                        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);
+                                                 "Increase the timeout with riscv set_command_timeout_sec.",
+                                                 riscv_command_timeout_sec, sbcs);
                                return ERROR_FAIL;
                        }
-
-                       if (dmi_op(target, &sbcs, &dmi_busy, DMI_OP_READ,
-                                               DM_SBCS, 0, false, true) != ERROR_OK)
+                       if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK)
                                return ERROR_FAIL;
                }
 
                if (get_field(sbcs, DM_SBCS_SBBUSYERROR)) {
-                       /* We wrote while the target was busy. Slow down and try again. */
-                       dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR);
+                       /* We wrote while the target was busy. */
+                       LOG_DEBUG("Sbbusyerror encountered during system bus write.");
+                       /* Clear the sticky error flag. */
+                       dmi_write(target, DM_SBCS, sbcs | DM_SBCS_SBBUSYERROR);
+                       /* Slow down before trying again. */
                        info->bus_master_write_delay += info->bus_master_write_delay / 10 + 1;
                }
 
                if (get_field(sbcs, DM_SBCS_SBBUSYERROR) || dmi_busy_encountered) {
+                       /* Recover from the case when the write commands were issued too fast.
+                        * Determine the address from which to resume writing. */
                        next_address = sb_read_address(target);
                        if (next_address < address) {
                                /* This should never happen, probably buggy hardware. */
-                               LOG_DEBUG("unexpected system bus address 0x%" TARGET_PRIxADDR,
-                                         next_address);
+                               LOG_DEBUG("unexpected sbaddress=0x%" TARGET_PRIxADDR
+                                                 " - buggy sbautoincrement in hw?", next_address);
+                               /* Fail the whole operation. */
                                return ERROR_FAIL;
                        }
-
+                       /* Try again - resume writing. */
                        continue;
                }
 
-               unsigned error = get_field(sbcs, DM_SBCS_SBERROR);
-               if (error != 0) {
-                       /* Some error indicating the bus access failed, but not because of
-                        * something we did wrong. */
+               unsigned int sberror = get_field(sbcs, DM_SBCS_SBERROR);
+               if (sberror != 0) {
+                       /* Sberror indicates the bus access failed, but not because we issued the writes
+                        * too fast. Cannot recover. Sbaddress holds the address where the error occurred
+                        * (unless sbautoincrement in the HW is buggy).
+                        */
+                       target_addr_t sbaddress = sb_read_address(target);
+                       LOG_DEBUG("System bus access failed with sberror=%u (sbaddress=0x%" TARGET_PRIxADDR ")",
+                                         sberror, sbaddress);
+                       if (sbaddress < address) {
+                               /* This should never happen, probably buggy hardware.
+                                * Make a note to the user not to trust the sbaddress value. */
+                               LOG_DEBUG("unexpected sbaddress=0x%" TARGET_PRIxADDR
+                                                 " - buggy sbautoincrement in hw?", next_address);
+                       }
+                       /* Clear the sticky error flag */
                        dmi_write(target, DM_SBCS, DM_SBCS_SBERROR);
+                       /* Fail the whole operation */
                        return ERROR_FAIL;
                }
        }
@@ -3560,26 +3986,62 @@ error:
 static int write_memory(struct target *target, target_addr_t address,
                uint32_t size, uint32_t count, const uint8_t *buffer)
 {
+       if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) {
+               LOG_ERROR("BUG: Unsupported size for memory write: %d", size);
+               return ERROR_FAIL;
+       }
+
+       int ret = ERROR_FAIL;
+       RISCV_INFO(r);
        RISCV013_INFO(info);
 
-       if (has_sufficient_progbuf(target, 3) && !riscv_prefer_sba)
-               return write_memory_progbuf(target, address, size, count, buffer);
+       char *progbuf_result = "disabled";
+       char *sysbus_result = "disabled";
+       char *abstract_result = "disabled";
 
-       if ((get_field(info->sbcs, DM_SBCS_SBACCESS8) && size == 1) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS16) && size == 2) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS32) && size == 4) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS64) && size == 8) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS128) && size == 16)) {
-               if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0)
-                       return write_memory_bus_v0(target, address, size, count, buffer);
-               else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1)
-                       return write_memory_bus_v1(target, address, size, count, buffer);
-       }
+       for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) {
+               int method = r->mem_access_methods[i];
 
-       if (has_sufficient_progbuf(target, 3))
-               return write_memory_progbuf(target, address, size, count, buffer);
+               if (method == RISCV_MEM_ACCESS_PROGBUF) {
+                       if (mem_should_skip_progbuf(target, address, size, false, &progbuf_result))
+                               continue;
+
+                       ret = write_memory_progbuf(target, address, size, count, buffer);
 
-       return write_memory_abstract(target, address, size, count, buffer);
+                       if (ret != ERROR_OK)
+                               progbuf_result = "failed";
+               } else if (method == RISCV_MEM_ACCESS_SYSBUS) {
+                       if (mem_should_skip_sysbus(target, address, size, 0, false, &sysbus_result))
+                               continue;
+
+                       if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0)
+                               ret = write_memory_bus_v0(target, address, size, count, buffer);
+                       else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1)
+                               ret = write_memory_bus_v1(target, address, size, count, buffer);
+
+                       if (ret != ERROR_OK)
+                               sysbus_result = "failed";
+               } else if (method == RISCV_MEM_ACCESS_ABSTRACT) {
+                       if (mem_should_skip_abstract(target, address, size, 0, false, &abstract_result))
+                               continue;
+
+                       ret = write_memory_abstract(target, address, size, count, buffer);
+
+                       if (ret != ERROR_OK)
+                               abstract_result = "failed";
+               } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED)
+                       /* No further mem access method to try. */
+                       break;
+
+               log_mem_access_result(target, ret == ERROR_OK, method, false);
+
+               if (ret == ERROR_OK)
+                       return ret;
+       }
+
+       LOG_ERROR("Target %s: Failed to write memory (addr=0x%" PRIx64 ")", target_name(target), address);
+       LOG_ERROR("  progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result);
+       return ret;
 }
 
 static int arch_state(struct target *target)
@@ -3603,17 +4065,18 @@ struct target_type riscv013_target = {
 
        .write_memory = write_memory,
 
-       .arch_state = arch_state,
+       .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)
+               riscv_reg_t *value, int rid)
 {
-       LOG_DEBUG("[%d] reading register %s on hart %d", target->coreid,
-                       gdb_regno_name(rid), hid);
+       LOG_DEBUG("[%s] reading register %s", target_name(target),
+                       gdb_regno_name(rid));
 
-       riscv_set_current_hartid(target, hid);
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
 
        int result = ERROR_OK;
        if (rid == GDB_REGNO_PC) {
@@ -3624,7 +4087,8 @@ static int riscv013_get_register(struct target *target,
                uint64_t dcsr;
                /* TODO: move this into riscv.c. */
                result = register_read(target, &dcsr, GDB_REGNO_DCSR);
-               *value = get_field(dcsr, CSR_DCSR_PRV);
+               *value = set_field(0, VIRT_PRIV_V, get_field(dcsr, CSR_DCSR_V));
+               *value = set_field(*value, VIRT_PRIV_PRV, get_field(dcsr, CSR_DCSR_PRV));
        } else {
                result = register_read(target, value, rid);
                if (result != ERROR_OK)
@@ -3634,12 +4098,11 @@ static int riscv013_get_register(struct target *target,
        return result;
 }
 
-static int riscv013_set_register(struct target *target, int hid, int rid, uint64_t value)
+static int riscv013_set_register(struct target *target, int rid, uint64_t value)
 {
-       LOG_DEBUG("[%d] writing 0x%" PRIx64 " to register %s on hart %d",
-                       target->coreid, value, gdb_regno_name(rid), hid);
-
-       riscv_set_current_hartid(target, hid);
+       riscv013_select_current_hart(target);
+       LOG_DEBUG("[%d] writing 0x%" PRIx64 " to register %s",
+                       target->coreid, value, gdb_regno_name(rid));
 
        if (rid <= GDB_REGNO_XPR31) {
                return register_write_direct(target, rid, value);
@@ -3657,7 +4120,8 @@ static int riscv013_set_register(struct target *target, int hid, int rid, uint64
        } else if (rid == GDB_REGNO_PRIV) {
                uint64_t dcsr;
                register_read(target, &dcsr, GDB_REGNO_DCSR);
-               dcsr = set_field(dcsr, CSR_DCSR_PRV, value);
+               dcsr = set_field(dcsr, CSR_DCSR_PRV, get_field(value, VIRT_PRIV_PRV));
+               dcsr = set_field(dcsr, CSR_DCSR_V, get_field(value, VIRT_PRIV_V));
                return register_write_direct(target, GDB_REGNO_DCSR, dcsr);
        } else {
                return register_write_direct(target, rid, value);
@@ -3748,10 +4212,8 @@ static int riscv013_halt_prep(struct target *target)
 static int riscv013_halt_go(struct target *target)
 {
        bool use_hasel = false;
-       if (!riscv_rtos_enabled(target)) {
-               if (select_prepped_harts(target, &use_hasel) != ERROR_OK)
-                       return ERROR_FAIL;
-       }
+       if (select_prepped_harts(target, &use_hasel) != ERROR_OK)
+               return ERROR_FAIL;
 
        RISCV_INFO(r);
        LOG_DEBUG("halting hart %d", r->current_hartid);
@@ -3802,10 +4264,8 @@ static int riscv013_halt_go(struct target *target)
 static int riscv013_resume_go(struct target *target)
 {
        bool use_hasel = false;
-       if (!riscv_rtos_enabled(target)) {
-               if (select_prepped_harts(target, &use_hasel) != ERROR_OK)
-                       return ERROR_FAIL;
-       }
+       if (select_prepped_harts(target, &use_hasel) != ERROR_OK)
+               return ERROR_FAIL;
 
        return riscv013_step_or_resume_current_hart(target, false, use_hasel);
 }
@@ -3865,6 +4325,8 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
        if (result != ERROR_OK)
                return RISCV_HALT_UNKNOWN;
 
+       LOG_DEBUG("dcsr.cause: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE));
+
        switch (get_field(dcsr, CSR_DCSR_CAUSE)) {
        case CSR_DCSR_CAUSE_SWBP:
                return RISCV_HALT_BREAKPOINT;
@@ -3884,7 +4346,7 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
                return RISCV_HALT_GROUP;
        }
 
-       LOG_ERROR("Unknown DCSR cause field: %x", (int)get_field(dcsr, CSR_DCSR_CAUSE));
+       LOG_ERROR("Unknown DCSR cause field: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE));
        LOG_ERROR("  dcsr=0x%016lx", (long)dcsr);
        return RISCV_HALT_UNKNOWN;
 }
@@ -4400,477 +4862,3 @@ void riscv013_clear_abstract_error(struct target *target)
        /* Clear the error status. */
        dmi_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR);
 }
-
-#ifdef _WIN32
-#define FILE_SEP '\\'
-#else
-#define FILE_SEP '/'
-#endif
-#define COMPLIANCE_TEST(b, message) \
-{ \
-       const char *last_sep = strrchr(__FILE__, FILE_SEP); \
-       const char *fname = (!last_sep ? __FILE__ : last_sep + 1); \
-       LOG_INFO("Executing test %d (%s:%d): %s", total_tests, fname, __LINE__, message); \
-       int pass = 0;               \
-       if (b) {                    \
-               pass = 1;           \
-               passed_tests++;     \
-       }                           \
-       LOG_INFO("  %s", (pass) ? "PASSED" : "FAILED"); \
-       assert(pass);               \
-       total_tests++;              \
-}
-
-#define COMPLIANCE_MUST_PASS(b) COMPLIANCE_TEST(ERROR_OK == (b), "Regular calls must return ERROR_OK")
-
-#define COMPLIANCE_READ(target, addr, value) COMPLIANCE_MUST_PASS(dmi_read(target, addr, value))
-#define COMPLIANCE_WRITE(target, addr, value) COMPLIANCE_MUST_PASS(dmi_write(target, addr, value))
-
-#define COMPLIANCE_CHECK_RO(target, addr)                               \
-{                                                                       \
-       uint32_t orig;                                                      \
-       uint32_t inverse;                                                   \
-       COMPLIANCE_READ(target, &orig, addr);                               \
-       COMPLIANCE_WRITE(target, addr, ~orig);                              \
-       COMPLIANCE_READ(target, &inverse, addr);                            \
-       COMPLIANCE_TEST(orig == inverse, "Register must be read-only");     \
-}
-
-int riscv013_test_compliance(struct target *target)
-{
-       LOG_INFO("Basic compliance test against RISC-V Debug Spec v0.13");
-       LOG_INFO("This test is not complete, and not well supported.");
-       LOG_INFO("Your core might pass this test without being compliant.");
-       LOG_INFO("Your core might fail this test while being compliant.");
-       LOG_INFO("Use your judgment, and please contribute improvements.");
-
-       if (!riscv_rtos_enabled(target)) {
-               LOG_ERROR("Please run with -rtos riscv to run compliance test.");
-               return ERROR_FAIL;
-       }
-
-       if (!target_was_examined(target)) {
-               LOG_ERROR("Cannot run compliance test, because target has not yet "
-                       "been examined, or the examination failed.\n");
-               return ERROR_FAIL;
-       }
-
-       int total_tests = 0;
-       int passed_tests = 0;
-
-       uint32_t dmcontrol_orig = DM_DMCONTROL_DMACTIVE;
-       uint32_t dmcontrol;
-       uint32_t testvar;
-       uint32_t testvar_read;
-       riscv_reg_t value;
-       RISCV013_INFO(info);
-
-       /* All the bits of HARTSEL are covered by the examine sequence. */
-
-       /* hartreset */
-       /* This field is optional. Either we can read and write it to 1/0,
-       or it is tied to 0. This check doesn't really do anything, but
-       it does attempt to set the bit to 1 and then back to 0, which needs to
-       work if its implemented. */
-       COMPLIANCE_WRITE(target, DM_DMCONTROL, set_field(dmcontrol_orig, DM_DMCONTROL_HARTRESET, 1));
-       COMPLIANCE_WRITE(target, DM_DMCONTROL, set_field(dmcontrol_orig, DM_DMCONTROL_HARTRESET, 0));
-       COMPLIANCE_READ(target, &dmcontrol, DM_DMCONTROL);
-       COMPLIANCE_TEST((get_field(dmcontrol, DM_DMCONTROL_HARTRESET) == 0),
-                       "DMCONTROL.hartreset can be 0 or RW.");
-
-       /* hasel */
-       COMPLIANCE_WRITE(target, DM_DMCONTROL, set_field(dmcontrol_orig, DM_DMCONTROL_HASEL, 1));
-       COMPLIANCE_WRITE(target, DM_DMCONTROL, set_field(dmcontrol_orig, DM_DMCONTROL_HASEL, 0));
-       COMPLIANCE_READ(target, &dmcontrol, DM_DMCONTROL);
-       COMPLIANCE_TEST((get_field(dmcontrol, DM_DMCONTROL_HASEL) == 0),
-                       "DMCONTROL.hasel can be 0 or RW.");
-       /* TODO: test that hamask registers exist if hasel does. */
-
-       /* haltreq */
-       COMPLIANCE_MUST_PASS(riscv_halt(target));
-       /* This bit is not actually readable according to the spec, so nothing to check.*/
-
-       /* DMSTATUS */
-       COMPLIANCE_CHECK_RO(target, DM_DMSTATUS);
-
-       /* resumereq */
-       /* This bit is not actually readable according to the spec, so nothing to check.*/
-       COMPLIANCE_MUST_PASS(riscv_resume(target, true, 0, false, false, false));
-
-       /* Halt all harts again so the test can continue.*/
-       COMPLIANCE_MUST_PASS(riscv_halt(target));
-
-       /* HARTINFO: Read-Only. This is per-hart, so need to adjust hartsel. */
-       uint32_t hartinfo;
-       COMPLIANCE_READ(target, &hartinfo, DM_HARTINFO);
-       for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) {
-               COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel));
-
-               COMPLIANCE_CHECK_RO(target, DM_HARTINFO);
-
-               /* $dscratch CSRs */
-               uint32_t nscratch = get_field(hartinfo, DM_HARTINFO_NSCRATCH);
-               for (unsigned int d = 0; d < nscratch; d++) {
-                       riscv_reg_t testval, testval_read;
-                       /* Because DSCRATCH0 is not guaranteed to last across PB executions, need to put
-                       this all into one PB execution. Which may not be possible on all implementations.*/
-                       if (info->progbufsize >= 5) {
-                               for (testval = 0x0011223300112233;
-                                                testval != 0xDEAD;
-                                                testval = testval == 0x0011223300112233 ? ~testval : 0xDEAD) {
-                                       COMPLIANCE_TEST(register_write_direct(target, GDB_REGNO_S0, testval) == ERROR_OK,
-                                                       "Need to be able to write S0 in order to test DSCRATCH0.");
-                                       struct riscv_program program32;
-                                       riscv_program_init(&program32, target);
-                                       riscv_program_csrw(&program32, GDB_REGNO_S0, GDB_REGNO_DSCRATCH0 + d);
-                                       riscv_program_csrr(&program32, GDB_REGNO_S1, GDB_REGNO_DSCRATCH0 + d);
-                                       riscv_program_fence(&program32);
-                                       riscv_program_ebreak(&program32);
-                                       COMPLIANCE_TEST(riscv_program_exec(&program32, target) == ERROR_OK,
-                                                       "Accessing DSCRATCH0 with program buffer should succeed.");
-                                       COMPLIANCE_TEST(register_read_direct(target, &testval_read, GDB_REGNO_S1) == ERROR_OK,
-                                                       "Need to be able to read S1 in order to test DSCRATCH0.");
-                                       if (riscv_xlen(target) > 32) {
-                                               COMPLIANCE_TEST(testval == testval_read,
-                                                               "All DSCRATCH0 registers in HARTINFO must be R/W.");
-                                       } else {
-                                               COMPLIANCE_TEST(testval_read == (testval & 0xFFFFFFFF),
-                                                               "All DSCRATCH0 registers in HARTINFO must be R/W.");
-                                       }
-                               }
-                       }
-               }
-               /* TODO: dataaccess */
-               if (get_field(hartinfo, DM_HARTINFO_DATAACCESS)) {
-                       /* TODO: Shadowed in memory map. */
-                       /* TODO: datasize */
-                       /* TODO: dataaddr */
-               } else {
-                       /* TODO: Shadowed in CSRs. */
-                       /* TODO: datasize */
-                       /* TODO: dataaddr */
-               }
-
-       }
-
-       /* HALTSUM -- TODO: More than 32 harts. Would need to loop over this to set hartsel */
-       /* TODO: HALTSUM2, HALTSUM3 */
-       /* HALTSUM0 */
-       uint32_t expected_haltsum0 = 0;
-       for (int i = 0; i < MIN(riscv_count_harts(target), 32); i++)
-               expected_haltsum0 |= (1 << i);
-
-       COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM0);
-       COMPLIANCE_TEST(testvar_read == expected_haltsum0,
-                       "HALTSUM0 should report summary of up to 32 halted harts");
-
-       COMPLIANCE_WRITE(target, DM_HALTSUM0, 0xffffffff);
-       COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM0);
-       COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O");
-
-       COMPLIANCE_WRITE(target, DM_HALTSUM0, 0x0);
-       COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM0);
-       COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O");
-
-       /* HALTSUM1 */
-       uint32_t expected_haltsum1 = 0;
-       for (int i = 0; i < MIN(riscv_count_harts(target), 1024); i += 32)
-               expected_haltsum1 |= (1 << (i/32));
-
-       COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM1);
-       COMPLIANCE_TEST(testvar_read == expected_haltsum1,
-                       "HALTSUM1 should report summary of up to 1024 halted harts");
-
-       COMPLIANCE_WRITE(target, DM_HALTSUM1, 0xffffffff);
-       COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM1);
-       COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O");
-
-       COMPLIANCE_WRITE(target, DM_HALTSUM1, 0x0);
-       COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM1);
-       COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O");
-
-       /* TODO: HAWINDOWSEL */
-
-       /* TODO: HAWINDOW */
-
-       /* ABSTRACTCS */
-
-       uint32_t abstractcs;
-       COMPLIANCE_READ(target, &abstractcs, DM_ABSTRACTCS);
-
-       /* Check that all reported Data Words are really R/W */
-       for (int invert = 0; invert < 2; invert++) {
-               for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) {
-                       testvar = (i + 1) * 0x11111111;
-                       if (invert)
-                               testvar = ~testvar;
-                       COMPLIANCE_WRITE(target, DM_DATA0 + i, testvar);
-               }
-               for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) {
-                       testvar = (i + 1) * 0x11111111;
-                       if (invert)
-                               testvar = ~testvar;
-                       COMPLIANCE_READ(target, &testvar_read, DM_DATA0 + i);
-                       COMPLIANCE_TEST(testvar_read == testvar, "All reported DATA words must be R/W");
-               }
-       }
-
-       /* Check that all reported ProgBuf words are really R/W */
-       for (int invert = 0; invert < 2; invert++) {
-               for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) {
-                       testvar = (i + 1) * 0x11111111;
-                       if (invert)
-                               testvar = ~testvar;
-                       COMPLIANCE_WRITE(target, DM_PROGBUF0 + i, testvar);
-               }
-               for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) {
-                       testvar = (i + 1) * 0x11111111;
-                       if (invert)
-                               testvar = ~testvar;
-                       COMPLIANCE_READ(target, &testvar_read, DM_PROGBUF0 + i);
-                       COMPLIANCE_TEST(testvar_read == testvar, "All reported PROGBUF words must be R/W");
-               }
-       }
-
-       /* TODO: Cause and clear all error types */
-
-       /* COMMAND
-       According to the spec, this register is only W, so can't really check the read result.
-       But at any rate, this is not legal and should cause an error. */
-       COMPLIANCE_WRITE(target, DM_COMMAND, 0xAAAAAAAA);
-       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS);
-       COMPLIANCE_TEST(get_field(testvar_read, DM_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED,
-                       "Illegal COMMAND should result in UNSUPPORTED");
-       COMPLIANCE_WRITE(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR);
-
-       COMPLIANCE_WRITE(target, DM_COMMAND, 0x55555555);
-       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS);
-       COMPLIANCE_TEST(get_field(testvar_read, DM_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED,
-                       "Illegal COMMAND should result in UNSUPPORTED");
-       COMPLIANCE_WRITE(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR);
-
-       /* Basic Abstract Commands */
-       for (unsigned int i = 1; i < 32; i = i << 1) {
-               riscv_reg_t testval =   i | ((i + 1ULL) << 32);
-               riscv_reg_t testval_read;
-               COMPLIANCE_TEST(register_write_direct(target, GDB_REGNO_ZERO + i, testval) == ERROR_OK,
-                               "GPR Writes should be supported.");
-               COMPLIANCE_MUST_PASS(write_abstract_arg(target, 0, 0xDEADBEEFDEADBEEF, 64));
-               COMPLIANCE_TEST(register_read_direct(target, &testval_read, GDB_REGNO_ZERO + i) == ERROR_OK,
-                               "GPR Reads should be supported.");
-               if (riscv_xlen(target) > 32) {
-                       /* Dummy comment to satisfy linter, since removing the branches here doesn't actually compile. */
-                       COMPLIANCE_TEST(testval == testval_read, "GPR Reads and writes should be supported.");
-               } else {
-                       /* Dummy comment to satisfy linter, since removing the branches here doesn't actually compile. */
-                       COMPLIANCE_TEST((testval & 0xFFFFFFFF) == testval_read, "GPR Reads and writes should be supported.");
-               }
-       }
-
-       /* ABSTRACTAUTO
-       See which bits are actually writable */
-       COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0xFFFFFFFF);
-       uint32_t abstractauto;
-       uint32_t busy;
-       COMPLIANCE_READ(target, &abstractauto, DM_ABSTRACTAUTO);
-       COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0x0);
-       if (abstractauto > 0) {
-               /* This mechanism only works when you have a reasonable sized progbuf, which is not
-               a true compliance requirement. */
-               if (info->progbufsize >= 3) {
-
-                       testvar = 0;
-                       COMPLIANCE_TEST(register_write_direct(target, GDB_REGNO_S0, 0) == ERROR_OK,
-                                       "Need to be able to write S0 to test ABSTRACTAUTO");
-                       struct riscv_program program;
-                       COMPLIANCE_MUST_PASS(riscv_program_init(&program, target));
-                       /* This is also testing that WFI() is a NOP during debug mode. */
-                       COMPLIANCE_MUST_PASS(riscv_program_insert(&program, wfi()));
-                       COMPLIANCE_MUST_PASS(riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, 1));
-                       COMPLIANCE_MUST_PASS(riscv_program_ebreak(&program));
-                       COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0x0);
-                       COMPLIANCE_MUST_PASS(riscv_program_exec(&program, target));
-                       testvar++;
-                       COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0xFFFFFFFF);
-                       COMPLIANCE_READ(target, &abstractauto, DM_ABSTRACTAUTO);
-                       uint32_t autoexec_data = get_field(abstractauto, DM_ABSTRACTAUTO_AUTOEXECDATA);
-                       uint32_t autoexec_progbuf = get_field(abstractauto, DM_ABSTRACTAUTO_AUTOEXECPROGBUF);
-                       for (unsigned int i = 0; i < 12; i++) {
-                               COMPLIANCE_READ(target, &testvar_read, DM_DATA0 + i);
-                               do {
-                                       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS);
-                                       busy = get_field(testvar_read, DM_ABSTRACTCS_BUSY);
-                               } while (busy);
-                               if (autoexec_data & (1 << i)) {
-                                       COMPLIANCE_TEST(i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT),
-                                                       "AUTOEXEC may be writable up to DATACOUNT bits.");
-                                       testvar++;
-                               }
-                       }
-                       for (unsigned int i = 0; i < 16; i++) {
-                               COMPLIANCE_READ(target, &testvar_read, DM_PROGBUF0 + i);
-                               do {
-                                       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS);
-                                       busy = get_field(testvar_read, DM_ABSTRACTCS_BUSY);
-                               } while (busy);
-                               if (autoexec_progbuf & (1 << i)) {
-                                       COMPLIANCE_TEST(i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE),
-                                                       "AUTOEXEC may be writable up to PROGBUFSIZE bits.");
-                                       testvar++;
-                               }
-                       }
-
-                       COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0);
-                       COMPLIANCE_TEST(register_read_direct(target, &value, GDB_REGNO_S0) == ERROR_OK,
-                                       "Need to be able to read S0 to test ABSTRACTAUTO");
-
-                       COMPLIANCE_TEST(testvar == value,
-                                       "ABSTRACTAUTO should cause COMMAND to run the expected number of times.");
-               }
-       }
-
-       /* Single-Step each hart. */
-       for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) {
-               COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel));
-               COMPLIANCE_MUST_PASS(riscv013_on_step(target));
-               COMPLIANCE_MUST_PASS(riscv013_step_current_hart(target));
-               COMPLIANCE_TEST(riscv_halt_reason(target, hartsel) == RISCV_HALT_SINGLESTEP,
-                               "Single Step should result in SINGLESTEP");
-       }
-
-       /* Core Register Tests */
-       uint64_t bogus_dpc = 0xdeadbeef;
-       for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) {
-               COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel));
-
-               /* DCSR Tests */
-               COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DCSR, 0x0));
-               COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DCSR));
-               COMPLIANCE_TEST(value != 0,     "Not all bits in DCSR are writable by Debugger");
-               COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DCSR, 0xFFFFFFFF));
-               COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DCSR));
-               COMPLIANCE_TEST(value != 0,     "At least some bits in DCSR must be 1");
-
-               /* DPC. Note that DPC is sign-extended. */
-               riscv_reg_t dpcmask = 0xFFFFFFFCUL;
-               riscv_reg_t dpc;
-
-               if (riscv_xlen(target) > 32)
-                       dpcmask |= (0xFFFFFFFFULL << 32);
-
-               if (riscv_supports_extension(target, riscv_current_hartid(target), 'C'))
-                       dpcmask |= 0x2;
-
-               COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DPC, dpcmask));
-               COMPLIANCE_MUST_PASS(register_read_direct(target, &dpc, GDB_REGNO_DPC));
-               COMPLIANCE_TEST(dpcmask == dpc,
-                               "DPC must be sign-extended to XLEN and writable to all-1s (except the least significant bits)");
-               COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DPC, 0));
-               COMPLIANCE_MUST_PASS(register_read_direct(target, &dpc, GDB_REGNO_DPC));
-               COMPLIANCE_TEST(dpc == 0, "DPC must be writable to 0.");
-               if (hartsel == 0)
-                       bogus_dpc = dpc; /* For a later test step */
-       }
-
-       /* NDMRESET
-       Asserting non-debug module reset should not reset Debug Module state.
-       But it should reset Hart State, e.g. DPC should get a different value.
-       Also make sure that DCSR reports cause of 'HALT' even though previously we single-stepped.
-       */
-
-       /* Write some registers. They should not be impacted by ndmreset. */
-       COMPLIANCE_WRITE(target, DM_COMMAND, 0xFFFFFFFF);
-
-       for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) {
-               testvar = (i + 1) * 0x11111111;
-               COMPLIANCE_WRITE(target, DM_PROGBUF0 + i, testvar);
-       }
-
-       for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) {
-               testvar = (i + 1) * 0x11111111;
-               COMPLIANCE_WRITE(target, DM_DATA0 + i, testvar);
-       }
-
-       COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0xFFFFFFFF);
-       COMPLIANCE_READ(target, &abstractauto, DM_ABSTRACTAUTO);
-
-       /* Pulse reset. */
-       target->reset_halt = true;
-       COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, 0));
-       COMPLIANCE_TEST(assert_reset(target) == ERROR_OK, "Must be able to assert NDMRESET");
-       COMPLIANCE_TEST(deassert_reset(target) == ERROR_OK, "Must be able to deassert NDMRESET");
-
-       /* Verify that most stuff is not affected by ndmreset. */
-       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS);
-       COMPLIANCE_TEST(get_field(testvar_read, DM_ABSTRACTCS_CMDERR)   == CMDERR_NOT_SUPPORTED,
-                       "NDMRESET should not affect DM_ABSTRACTCS");
-       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTAUTO);
-       COMPLIANCE_TEST(testvar_read == abstractauto, "NDMRESET should not affect DM_ABSTRACTAUTO");
-
-       /* Clean up to avoid future test failures */
-       COMPLIANCE_WRITE(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR);
-       COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0);
-
-       for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) {
-               testvar = (i + 1) * 0x11111111;
-               COMPLIANCE_READ(target, &testvar_read, DM_PROGBUF0 + i);
-               COMPLIANCE_TEST(testvar_read == testvar, "PROGBUF words must not be affected by NDMRESET");
-       }
-
-       for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) {
-               testvar = (i + 1) * 0x11111111;
-               COMPLIANCE_READ(target, &testvar_read, DM_DATA0 + i);
-               COMPLIANCE_TEST(testvar_read == testvar, "DATA words must not be affected by NDMRESET");
-       }
-
-       /* Verify that DPC *is* affected by ndmreset. Since we don't know what it *should* be,
-       just verify that at least it's not the bogus value anymore. */
-
-       COMPLIANCE_TEST(bogus_dpc != 0xdeadbeef, "BOGUS DPC should have been set somehow (bug in compliance test)");
-       COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DPC));
-       COMPLIANCE_TEST(bogus_dpc != value, "NDMRESET should move DPC to reset value.");
-
-       COMPLIANCE_TEST(riscv_halt_reason(target, 0) == RISCV_HALT_INTERRUPT,
-                       "After NDMRESET halt, DCSR should report cause of halt");
-
-       /* DMACTIVE -- deasserting DMACTIVE should reset all the above values. */
-
-       /* Toggle dmactive */
-       COMPLIANCE_WRITE(target, DM_DMCONTROL, 0);
-       COMPLIANCE_WRITE(target, DM_DMCONTROL, DM_DMCONTROL_DMACTIVE);
-       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS);
-       COMPLIANCE_TEST(get_field(testvar_read, DM_ABSTRACTCS_CMDERR)   == 0, "ABSTRACTCS.cmderr should reset to 0");
-       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTAUTO);
-       COMPLIANCE_TEST(testvar_read == 0, "ABSTRACTAUTO should reset to 0");
-
-       for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) {
-               COMPLIANCE_READ(target, &testvar_read, DM_PROGBUF0 + i);
-               COMPLIANCE_TEST(testvar_read == 0, "PROGBUF words should reset to 0");
-       }
-
-       for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) {
-               COMPLIANCE_READ(target, &testvar_read, DM_DATA0 + i);
-               COMPLIANCE_TEST(testvar_read == 0, "DATA words should reset to 0");
-       }
-
-       /*
-       * TODO:
-       * DCSR.cause priorities
-       * DCSR.stoptime/stopcycle
-       * DCSR.stepie
-       * DCSR.ebreak
-       * DCSR.prv
-       */
-
-       /* Halt every hart for any follow-up tests*/
-       COMPLIANCE_MUST_PASS(riscv_halt(target));
-
-       uint32_t failed_tests = total_tests - passed_tests;
-       if (total_tests == passed_tests) {
-               LOG_INFO("ALL TESTS PASSED\n");
-               return ERROR_OK;
-       } else {
-               LOG_INFO("%d TESTS FAILED\n", failed_tests);
-               return ERROR_FAIL;
-       }
-}