esirisc: support eSi-RISC targets
[fw/openocd] / src / target / esirisc.c
diff --git a/src/target/esirisc.c b/src/target/esirisc.c
new file mode 100644 (file)
index 0000000..38100e8
--- /dev/null
@@ -0,0 +1,1787 @@
+/***************************************************************************
+ *   Copyright (C) 2018 by Square, Inc.                                    *
+ *   Steven Stallion <stallion@squareup.com>                               *
+ *   James Zhao <hjz@squareup.com>                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <helper/binarybuffer.h>
+#include <helper/command.h>
+#include <helper/log.h>
+#include <helper/time_support.h>
+#include <helper/types.h>
+#include <jtag/interface.h>
+#include <target/breakpoints.h>
+#include <target/register.h>
+#include <target/target.h>
+#include <target/target_type.h>
+
+#include "esirisc.h"
+
+#define RESET_TIMEOUT  5000    /* 5s */
+#define STEP_TIMEOUT   1000    /* 1s */
+
+/*
+ * eSi-RISC targets support a configurable number of interrupts;
+ * up to 32 interrupts are supported.
+ */
+static const char * const esirisc_exceptions[] = {
+       "Reset", "HardwareFailure", "NMI", "InstBreakpoint", "DataBreakpoint",
+       "Unsupported", "PrivilegeViolation", "InstBusError", "DataBusError",
+       "AlignmentError", "ArithmeticError", "SystemCall", "MemoryManagement",
+       "Unrecoverable", "Reserved",
+
+       "Interrupt0", "Interrupt1", "Interrupt2", "Interrupt3",
+       "Interrupt4", "Interrupt5", "Interrupt6", "Interrupt7",
+       "Interrupt8", "Interrupt9", "Interrupt10", "Interrupt11",
+       "Interrupt12", "Interrupt13", "Interrupt14", "Interrupt15",
+       "Interrupt16", "Interrupt17", "Interrupt18", "Interrupt19",
+       "Interrupt20", "Interrupt21", "Interrupt22", "Interrupt23",
+       "Interrupt24", "Interrupt25", "Interrupt26", "Interrupt27",
+       "Interrupt28", "Interrupt29", "Interrupt30", "Interrupt31",
+};
+
+/*
+ * eSi-RISC targets support a configurable number of general purpose
+ * registers; 8, 16, and 32 registers are supported.
+ */
+static const struct {
+       enum esirisc_reg_num number;
+       const char *name;
+       enum reg_type type;
+       const char *group;
+} esirisc_regs[] = {
+       { ESIRISC_SP, "sp", REG_TYPE_DATA_PTR, "general" },
+       { ESIRISC_RA, "ra", REG_TYPE_INT, "general" },
+       { ESIRISC_R2, "r2", REG_TYPE_INT, "general" },
+       { ESIRISC_R3, "r3", REG_TYPE_INT, "general" },
+       { ESIRISC_R4, "r4", REG_TYPE_INT, "general" },
+       { ESIRISC_R5, "r5", REG_TYPE_INT, "general" },
+       { ESIRISC_R6, "r6", REG_TYPE_INT, "general" },
+       { ESIRISC_R7, "r7", REG_TYPE_INT, "general" },
+       { ESIRISC_R8, "r8", REG_TYPE_INT, "general" },
+       { ESIRISC_R9, "r9", REG_TYPE_INT, "general" },
+       { ESIRISC_R10, "r10", REG_TYPE_INT, "general" },
+       { ESIRISC_R11, "r11", REG_TYPE_INT, "general" },
+       { ESIRISC_R12, "r12", REG_TYPE_INT, "general" },
+       { ESIRISC_R13, "r13", REG_TYPE_INT, "general" },
+       { ESIRISC_R14, "r14", REG_TYPE_INT, "general" },
+       { ESIRISC_R15, "r15", REG_TYPE_INT, "general" },
+       { ESIRISC_R16, "r16", REG_TYPE_INT, "general" },
+       { ESIRISC_R17, "r17", REG_TYPE_INT, "general" },
+       { ESIRISC_R18, "r18", REG_TYPE_INT, "general" },
+       { ESIRISC_R19, "r19", REG_TYPE_INT, "general" },
+       { ESIRISC_R20, "r20", REG_TYPE_INT, "general" },
+       { ESIRISC_R21, "r21", REG_TYPE_INT, "general" },
+       { ESIRISC_R22, "r22", REG_TYPE_INT, "general" },
+       { ESIRISC_R23, "r23", REG_TYPE_INT, "general" },
+       { ESIRISC_R24, "r24", REG_TYPE_INT, "general" },
+       { ESIRISC_R25, "r25", REG_TYPE_INT, "general" },
+       { ESIRISC_R26, "r26", REG_TYPE_INT, "general" },
+       { ESIRISC_R27, "r27", REG_TYPE_INT, "general" },
+       { ESIRISC_R28, "r28", REG_TYPE_INT, "general" },
+       { ESIRISC_R29, "r29", REG_TYPE_INT, "general" },
+       { ESIRISC_R30, "r30", REG_TYPE_INT, "general" },
+       { ESIRISC_R31, "r31", REG_TYPE_INT, "general" },
+};
+
+/*
+ * Control and Status Registers (CSRs) are largely defined as belonging
+ * to the system register group. The exception to this rule are the PC
+ * and CAS registers, which belong to the general group. While debug is
+ * active, EPC, ECAS, and ETC must be used to read and write the PC,
+ * CAS, and TC CSRs, respectively.
+ */
+static const struct {
+       enum esirisc_reg_num number;
+       uint8_t bank;
+       uint8_t csr;
+       const char *name;
+       enum reg_type type;
+       const char *group;
+} esirisc_csrs[] = {
+       { ESIRISC_PC, CSR_THREAD, CSR_THREAD_EPC, "PC", REG_TYPE_CODE_PTR, "general" }, /*  PC -> EPC  */
+       { ESIRISC_CAS, CSR_THREAD, CSR_THREAD_ECAS, "CAS", REG_TYPE_INT, "general" },   /* CAS -> ECAS */
+       { ESIRISC_TC, CSR_THREAD, CSR_THREAD_ETC, "TC", REG_TYPE_INT, "system" },               /*  TC -> ETC  */
+       { ESIRISC_ETA, CSR_THREAD, CSR_THREAD_ETA, "ETA", REG_TYPE_INT, "system" },
+       { ESIRISC_ETC, CSR_THREAD, CSR_THREAD_ETC, "ETC", REG_TYPE_INT, "system" },
+       { ESIRISC_EPC, CSR_THREAD, CSR_THREAD_EPC, "EPC", REG_TYPE_CODE_PTR, "system" },
+       { ESIRISC_ECAS, CSR_THREAD, CSR_THREAD_ECAS, "ECAS", REG_TYPE_INT, "system" },
+       { ESIRISC_EID, CSR_THREAD, CSR_THREAD_EID, "EID", REG_TYPE_INT, "system" },
+       { ESIRISC_ED, CSR_THREAD, CSR_THREAD_ED, "ED", REG_TYPE_INT, "system" },
+       { ESIRISC_IP, CSR_INTERRUPT, CSR_INTERRUPT_IP, "IP", REG_TYPE_INT, "system"},
+       { ESIRISC_IM, CSR_INTERRUPT, CSR_INTERRUPT_IM, "IM", REG_TYPE_INT, "system"},
+       { ESIRISC_IS, CSR_INTERRUPT, CSR_INTERRUPT_IS, "IS", REG_TYPE_INT, "system"},
+       { ESIRISC_IT, CSR_INTERRUPT, CSR_INTERRUPT_IT, "IT", REG_TYPE_INT, "system"},
+};
+
+static int esirisc_disable_interrupts(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       uint32_t etc;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, &etc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: ETC", target_name(target));
+               return retval;
+       }
+
+       etc &= ~(1<<0);         /* TC.I */
+
+       retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, etc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: ETC", target_name(target));
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+#if 0
+static int esirisc_enable_interrupts(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       uint32_t etc;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, &etc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: ETC", target_name(target));
+               return retval;
+       }
+
+       etc |= (1<<0);          /* TC.I */
+
+       retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, etc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: ETC", target_name(target));
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+#endif
+
+static int esirisc_save_interrupts(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+
+       LOG_DEBUG("-");
+
+       int retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC,
+                       &esirisc->etc_save);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: ETC", target_name(target));
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_restore_interrupts(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+
+       LOG_DEBUG("-");
+
+       int retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC,
+                       esirisc->etc_save);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: ETC", target_name(target));
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+#if 0
+static int esirisc_save_hwdc(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+
+       LOG_DEBUG("-");
+
+       int retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_HWDC,
+                       &esirisc->hwdc_save);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: HWDC", target_name(target));
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+#endif
+
+static int esirisc_restore_hwdc(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+
+       LOG_DEBUG("-");
+
+       int retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_HWDC,
+                       esirisc->hwdc_save);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: HWDC", target_name(target));
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_save_context(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+
+       LOG_DEBUG("-");
+
+       for (unsigned i = 0; i < esirisc->reg_cache->num_regs; ++i) {
+               struct reg *reg = esirisc->reg_cache->reg_list + i;
+               struct esirisc_reg *reg_info = reg->arch_info;
+
+               if (reg->exist && !reg->valid)
+                       reg_info->read(reg);
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_restore_context(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+
+       LOG_DEBUG("-");
+
+       for (unsigned i = 0; i < esirisc->reg_cache->num_regs; ++i) {
+               struct reg *reg = esirisc->reg_cache->reg_list + i;
+               struct esirisc_reg *reg_info = reg->arch_info;
+
+               if (reg->exist && reg->dirty)
+                       reg_info->write(reg);
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_flush_caches(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+
+       LOG_DEBUG("-");
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       int retval = esirisc_jtag_flush_caches(jtag_info);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to flush caches", target_name(target));
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_wait_debug_active(struct esirisc_common *esirisc, int ms)
+{
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int64_t t;
+
+       LOG_DEBUG("-");
+
+       t = timeval_ms();
+       for (;;) {
+               int retval = esirisc_jtag_enable_debug(jtag_info);
+               if (retval == ERROR_OK && esirisc_jtag_is_debug_active(jtag_info))
+                       return retval;
+
+               if ((timeval_ms() - t) > ms)
+                       return ERROR_TARGET_TIMEOUT;
+
+               alive_sleep(100);
+       }
+}
+
+static int esirisc_read_memory(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, uint8_t *buffer)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       int num_bits = 8 * size;
+       for (uint32_t i = 0; i < count; ++i) {
+               union esirisc_memory value;
+               void *value_p;
+
+               switch (size) {
+                       case sizeof(value.word):
+                               value_p = &value.word;
+                               retval = esirisc_jtag_read_word(jtag_info, address, value_p);
+                               break;
+
+                       case sizeof(value.hword):
+                               value_p = &value.hword;
+                               retval = esirisc_jtag_read_hword(jtag_info, address, value_p);
+                               break;
+
+                       case sizeof(value.byte):
+                               value_p = &value.byte;
+                               retval = esirisc_jtag_read_byte(jtag_info, address, value_p);
+                               break;
+
+                       default:
+                               LOG_ERROR("%s: unsupported size: %" PRIu32, target_name(target), size);
+                               return ERROR_FAIL;
+               }
+
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("%s: failed to read address: 0x%" TARGET_PRIxADDR, target_name(target),
+                                       address);
+                       return retval;
+               }
+
+               buf_cpy(value_p, buffer, num_bits);
+               address += size;
+               buffer += size;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_write_memory(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       int num_bits = 8 * size;
+       for (uint32_t i = 0; i < count; ++i) {
+               union esirisc_memory value;
+
+               switch (size) {
+                       case sizeof(value.word):
+                               value.word = buf_get_u32(buffer, 0, num_bits);
+                               retval = esirisc_jtag_write_word(jtag_info, address, value.word);
+                               break;
+
+                       case sizeof(value.hword):
+                               value.hword = buf_get_u32(buffer, 0, num_bits);
+                               retval = esirisc_jtag_write_hword(jtag_info, address, value.hword);
+                               break;
+
+                       case sizeof(value.byte):
+                               value.byte = buf_get_u32(buffer, 0, num_bits);
+                               retval = esirisc_jtag_write_byte(jtag_info, address, value.byte);
+                               break;
+
+                       default:
+                               LOG_ERROR("%s: unsupported size: %" PRIu32, target_name(target), size);
+                               return ERROR_FAIL;
+               }
+
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("%s: failed to write address: 0x%" TARGET_PRIxADDR, target_name(target),
+                                       address);
+                       return retval;
+               }
+
+               address += size;
+               buffer += size;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_checksum_memory(struct target *target, target_addr_t address,
+               uint32_t count, uint32_t *checksum)
+{
+       return ERROR_FAIL;      /* not supported */
+}
+
+static int esirisc_next_breakpoint(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct breakpoint **breakpoints_p = esirisc->breakpoints_p;
+       struct breakpoint **breakpoints_e = breakpoints_p + esirisc->num_breakpoints;
+
+       LOG_DEBUG("-");
+
+       for (int bp_index = 0; breakpoints_p < breakpoints_e; ++breakpoints_p, ++bp_index)
+               if (*breakpoints_p == NULL)
+                       return bp_index;
+
+       return -1;
+}
+
+static int esirisc_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int bp_index;
+       uint32_t ibc;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       /*
+        * The default linker scripts provided by the eSi-RISC toolchain do
+        * not specify attributes on memory regions, which results in
+        * incorrect application of software breakpoints by GDB. Targets
+        * must be configured with `gdb_breakpoint_override hard` as
+        * software breakpoints are not supported.
+        */
+       if (breakpoint->type != BKPT_HARD)
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+
+       bp_index = esirisc_next_breakpoint(target);
+       if (bp_index < 0) {
+               LOG_ERROR("%s: out of hardware breakpoints", target_name(target));
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       breakpoint->set = bp_index + 1;
+       esirisc->breakpoints_p[bp_index] = breakpoint;
+
+       /* specify instruction breakpoint address */
+       retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBAn + bp_index,
+                       breakpoint->address);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: IBA", target_name(target));
+               return retval;
+       }
+
+       /* enable instruction breakpoint */
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, &ibc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: IBC", target_name(target));
+               return retval;
+       }
+
+       ibc |= (1 << bp_index);         /* IBC.In */
+
+       retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, ibc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: IBC", target_name(target));
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_add_breakpoints(struct target *target)
+{
+       struct breakpoint *breakpoint = target->breakpoints;
+
+       LOG_DEBUG("-");
+
+       while (breakpoint != NULL) {
+               if (breakpoint->set == 0)
+                       esirisc_add_breakpoint(target, breakpoint);
+
+               breakpoint = breakpoint->next;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_remove_breakpoint(struct target *target, struct breakpoint *breakpoint)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int bp_index = breakpoint->set - 1;
+       uint32_t ibc;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       /* disable instruction breakpoint */
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, &ibc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: IBC", target_name(target));
+               return retval;
+       }
+
+       ibc &= ~(1 << bp_index);        /* IBC.In */
+
+       retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, ibc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: IBC", target_name(target));
+               return retval;
+       }
+
+       esirisc->breakpoints_p[bp_index] = NULL;
+       breakpoint->set = 0;
+
+       return ERROR_OK;
+}
+
+static int esirisc_remove_breakpoints(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+
+       LOG_DEBUG("-");
+
+       /* clear instruction breakpoints */
+       int retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, 0);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: IBC", target_name(target));
+               return retval;
+       }
+
+       memset(esirisc->breakpoints_p, 0, sizeof(esirisc->breakpoints_p));
+
+       return ERROR_OK;
+}
+
+static int esirisc_next_watchpoint(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct watchpoint **watchpoints_p = esirisc->watchpoints_p;
+       struct watchpoint **watchpoints_e = watchpoints_p + esirisc->num_watchpoints;
+
+       LOG_DEBUG("-");
+
+       for (int wp_index = 0; watchpoints_p < watchpoints_e; ++watchpoints_p, ++wp_index)
+               if (*watchpoints_p == NULL)
+                       return wp_index;
+
+       return -1;
+}
+
+static int esirisc_add_watchpoint(struct target *target, struct watchpoint *watchpoint)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int wp_index;
+       uint32_t dbs, dbc;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       wp_index = esirisc_next_watchpoint(target);
+       if (wp_index < 0) {
+               LOG_ERROR("%s: out of hardware watchpoints", target_name(target));
+               return ERROR_FAIL;
+       }
+
+       watchpoint->set = wp_index + 1;
+       esirisc->watchpoints_p[wp_index] = watchpoint;
+
+       /* specify data breakpoint address */
+       retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBAn + wp_index,
+                       watchpoint->address);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: DBA", target_name(target));
+               return retval;
+       }
+
+       /* specify data breakpoint size */
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBS, &dbs);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: DBS", target_name(target));
+               return retval;
+       }
+
+       uint32_t sn;
+       switch (watchpoint->length) {
+               case sizeof(uint64_t):
+                       sn = 0x3;
+                       break;
+               case sizeof(uint32_t):
+                       sn = 0x2;
+                       break;
+
+               case sizeof(uint16_t):
+                       sn = 0x1;
+                       break;
+
+               case sizeof(uint8_t):
+                       sn = 0x0;
+                       break;
+
+               default:
+                       LOG_ERROR("%s: unsupported length: %" PRIu32, target_name(target),
+                                       watchpoint->length);
+                       return ERROR_FAIL;
+       }
+
+       dbs |= (sn << (2 * wp_index));          /* DBS.Sn */
+
+       retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBS, dbs);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: DBS", target_name(target));
+               return retval;
+       }
+
+       /* enable data breakpoint */
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, &dbc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: DBC", target_name(target));
+               return retval;
+       }
+
+       uint32_t dn;
+       switch (watchpoint->rw) {
+               case WPT_READ:
+                       dn = 0x1;
+                       break;
+
+               case WPT_WRITE:
+                       dn = 0x2;
+                       break;
+
+               case WPT_ACCESS:
+                       dn = 0x3;
+                       break;
+
+               default:
+                       LOG_ERROR("%s: unsupported rw: %" PRId32, target_name(target),
+                                       watchpoint->rw);
+                       return ERROR_FAIL;
+       }
+
+       dbc |= (dn << (2 * wp_index));          /* DBC.Dn */
+
+       retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, dbc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: DBC", target_name(target));
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_add_watchpoints(struct target *target)
+{
+       struct watchpoint *watchpoint = target->watchpoints;
+
+       LOG_DEBUG("-");
+
+       while (watchpoint != NULL) {
+               if (watchpoint->set == 0)
+                       esirisc_add_watchpoint(target, watchpoint);
+
+               watchpoint = watchpoint->next;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_remove_watchpoint(struct target *target, struct watchpoint *watchpoint)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int wp_index = watchpoint->set - 1;
+       uint32_t dbc;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       /* disable data breakpoint */
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, &dbc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: DBC", target_name(target));
+               return retval;
+       }
+
+       dbc &= ~(0x3 << (2 * wp_index));        /* DBC.Dn */
+
+       retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, dbc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: DBC", target_name(target));
+               return retval;
+       }
+
+       esirisc->watchpoints_p[wp_index] = NULL;
+       watchpoint->set = 0;
+
+       return ERROR_OK;
+}
+
+static int esirisc_remove_watchpoints(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+
+       LOG_DEBUG("-");
+
+       /* clear data breakpoints */
+       int retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, 0);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: DBC", target_name(target));
+               return retval;
+       }
+
+       memset(esirisc->watchpoints_p, 0, sizeof(esirisc->watchpoints_p));
+
+       return ERROR_OK;
+}
+
+static int esirisc_halt(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+
+       LOG_DEBUG("-");
+
+       if (target->state == TARGET_HALTED)
+               return ERROR_OK;
+
+       int retval = esirisc_jtag_break(jtag_info);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to halt target", target_name(target));
+               return retval;
+       }
+
+       target->debug_reason = DBG_REASON_DBGRQ;
+
+       return ERROR_OK;
+}
+
+static int esirisc_disable_step(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       uint32_t dc;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, &dc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: DC", target_name(target));
+               return retval;
+       }
+
+       dc &= ~(1<<0);  /* DC.S */
+
+       retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, dc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: DC", target_name(target));
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_enable_step(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       uint32_t dc;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, &dc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: DC", target_name(target));
+               return retval;
+       }
+
+       dc |= (1<<0);   /* DC.S */
+
+       retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, dc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: DC", target_name(target));
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_resume_or_step(struct target *target, int current, target_addr_t address,
+               int handle_breakpoints, int debug_execution, bool step)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       struct breakpoint *breakpoint = NULL;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       if (!debug_execution) {
+               target_free_all_working_areas(target);
+               esirisc_add_breakpoints(target);
+               esirisc_add_watchpoints(target);
+       }
+
+       if (current)
+               address = buf_get_u32(esirisc->epc->value, 0, esirisc->epc->size);
+       else {
+               buf_set_u32(esirisc->epc->value, 0, esirisc->epc->size, address);
+               esirisc->epc->dirty = true;
+               esirisc->epc->valid = true;
+       }
+
+       esirisc_restore_context(target);
+
+       if (esirisc_has_cache(esirisc))
+               esirisc_flush_caches(target);
+
+       if (handle_breakpoints) {
+               breakpoint = breakpoint_find(target, address);
+               if (breakpoint != NULL)
+                       esirisc_remove_breakpoint(target, breakpoint);
+       }
+
+       if (step) {
+               esirisc_disable_interrupts(target);
+               esirisc_enable_step(target);
+               target->debug_reason = DBG_REASON_SINGLESTEP;
+       } else {
+               esirisc_disable_step(target);
+               esirisc_restore_interrupts(target);
+               target->debug_reason = DBG_REASON_NOTHALTED;
+       }
+
+       esirisc_restore_hwdc(target);
+
+       retval = esirisc_jtag_continue(jtag_info);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to resume target", target_name(target));
+               return retval;
+       }
+
+       register_cache_invalidate(esirisc->reg_cache);
+
+       if (!debug_execution) {
+               target->state = TARGET_RUNNING;
+               target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+       } else {
+               target->state = TARGET_DEBUG_RUNNING;
+               target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED);
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_resume(struct target *target, int current, target_addr_t address,
+               int handle_breakpoints, int debug_execution)
+{
+       LOG_DEBUG("-");
+
+       return esirisc_resume_or_step(target, current, address,
+                       handle_breakpoints, debug_execution, false);
+}
+
+static int esirisc_step(struct target *target, int current, target_addr_t address,
+               int handle_breakpoints)
+{
+       LOG_DEBUG("-");
+
+       return esirisc_resume_or_step(target, current, address,
+                       handle_breakpoints, 0, true);
+}
+
+static int esirisc_debug_step(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       esirisc_disable_interrupts(target);
+       esirisc_enable_step(target);
+
+       retval = esirisc_jtag_continue(jtag_info);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to resume target", target_name(target));
+               return retval;
+       }
+
+       retval = esirisc_wait_debug_active(esirisc, STEP_TIMEOUT);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: step timed out", target_name(target));
+               return retval;
+       }
+
+       esirisc_disable_step(target);
+       esirisc_restore_interrupts(target);
+
+       return ERROR_OK;
+}
+
+static int esirisc_debug_reset(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       retval = esirisc_jtag_assert_reset(jtag_info);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to assert reset", target_name(target));
+               return retval;
+       }
+
+       retval = esirisc_jtag_deassert_reset(jtag_info);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to deassert reset", target_name(target));
+               return retval;
+       }
+
+       retval = esirisc_wait_debug_active(esirisc, RESET_TIMEOUT);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: reset timed out", target_name(target));
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_debug_enable(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       retval = esirisc_jtag_enable_debug(jtag_info);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to enable debug mode", target_name(target));
+               return retval;
+       }
+
+       /*
+        * The debug clock is inactive until the first command is sent.
+        * If the target is stopped, we must first issue a reset before
+        * attempting further communication. This also handles unpowered
+        * targets, which will respond with all ones and appear active.
+        */
+       if (esirisc_jtag_is_stopped(jtag_info)) {
+               LOG_INFO("%s: debug clock inactive; attempting debug reset", target_name(target));
+               retval = esirisc_debug_reset(target);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               if (esirisc_jtag_is_stopped(jtag_info)) {
+                       LOG_ERROR("%s: target unresponsive; giving up", target_name(target));
+                       return ERROR_FAIL;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_debug_entry(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct breakpoint *breakpoint;
+
+       LOG_DEBUG("-");
+
+       esirisc_save_context(target);
+
+       if (esirisc_has_cache(esirisc))
+               esirisc_flush_caches(target);
+
+       if (target->debug_reason != DBG_REASON_SINGLESTEP) {
+               esirisc_save_interrupts(target);
+
+               uint32_t eid = buf_get_u32(esirisc->eid->value, 0, esirisc->eid->size);
+               switch (eid) {
+                       /*
+                        * InstBreakpoint exceptions are also raised when a core is
+                        * halted for debugging. The following is required to
+                        * determine if a breakpoint was encountered.
+                        */
+                       case EID_INST_BREAKPOINT:
+                               breakpoint = breakpoint_find(target,
+                                               buf_get_u32(esirisc->epc->value, 0, esirisc->epc->size));
+                               target->debug_reason = (breakpoint != NULL) ?
+                                               DBG_REASON_BREAKPOINT : DBG_REASON_DBGRQ;
+                               break;
+
+                       /*
+                        * eSi-RISC treats watchpoints similarly to breakpoints,
+                        * however GDB will not request to step over the current
+                        * instruction when a watchpoint fires. The following is
+                        * required to resume the target.
+                        */
+                       case EID_DATA_BREAKPOINT:
+                               esirisc_remove_watchpoints(target);
+                               esirisc_debug_step(target);
+                               esirisc_add_watchpoints(target);
+                               target->debug_reason = DBG_REASON_WATCHPOINT;
+                               break;
+
+                       default:
+                               target->debug_reason = DBG_REASON_DBGRQ;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_poll(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int retval;
+
+       retval = esirisc_jtag_enable_debug(jtag_info);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to poll target", target_name(target));
+               return retval;
+       }
+
+       if (esirisc_jtag_is_stopped(jtag_info)) {
+               LOG_ERROR("%s: target has stopped; reset required", target_name(target));
+               target->state = TARGET_UNKNOWN;
+               return ERROR_TARGET_FAILURE;
+       }
+
+       if (esirisc_jtag_is_debug_active(jtag_info)) {
+               if (target->state == TARGET_RUNNING || target->state == TARGET_RESET) {
+                       target->state = TARGET_HALTED;
+
+                       retval = esirisc_debug_entry(target);
+                       if (retval != ERROR_OK)
+                               return retval;
+
+                       target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+               }
+
+       } else if (target->state == TARGET_HALTED || target->state == TARGET_RESET) {
+               target->state = TARGET_RUNNING;
+               target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_assert_reset(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       if (jtag_get_reset_config() & RESET_HAS_SRST) {
+               jtag_add_reset(1, 1);
+               if ((jtag_get_reset_config() & RESET_SRST_PULLS_TRST) == 0)
+                       jtag_add_reset(0, 1);
+       } else {
+               esirisc_remove_breakpoints(target);
+               esirisc_remove_watchpoints(target);
+
+               retval = esirisc_jtag_assert_reset(jtag_info);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("%s: failed to assert reset", target_name(target));
+                       return retval;
+               }
+       }
+
+       target->state = TARGET_RESET;
+
+       register_cache_invalidate(esirisc->reg_cache);
+
+       return ERROR_OK;
+}
+
+static int esirisc_reset_entry(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       uint32_t eta, epc;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       /* read exception table address */
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETA, &eta);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: ETA", target_name(target));
+               return retval;
+       }
+
+       /* read reset entry point */
+       retval = esirisc_jtag_read_word(jtag_info, eta + ENTRY_RESET, &epc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read address: 0x%" TARGET_PRIxADDR, target_name(target),
+                               (target_addr_t)epc);
+               return retval;
+       }
+
+       /* write reset entry point */
+       retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_EPC, epc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: EPC", target_name(target));
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_deassert_reset(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       if (jtag_get_reset_config() & RESET_HAS_SRST) {
+               jtag_add_reset(0, 0);
+
+               retval = esirisc_debug_enable(target);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               retval = esirisc_debug_reset(target);
+               if (retval != ERROR_OK)
+                       return retval;
+
+       } else {
+               retval = esirisc_jtag_deassert_reset(jtag_info);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("%s: failed to deassert reset", target_name(target));
+                       return retval;
+               }
+       }
+
+       retval = esirisc_wait_debug_active(esirisc, RESET_TIMEOUT);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: reset timed out", target_name(target));
+               return retval;
+       }
+
+       retval = esirisc_reset_entry(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       esirisc_add_breakpoints(target);
+       esirisc_add_watchpoints(target);
+
+       esirisc_restore_hwdc(target);
+
+       if (!target->reset_halt) {
+               retval = esirisc_jtag_continue(jtag_info);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("%s: failed to resume target", target_name(target));
+                       return retval;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_arch_state(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       uint32_t epc = buf_get_u32(esirisc->epc->value, 0, esirisc->epc->size);
+       uint32_t ecas = buf_get_u32(esirisc->ecas->value, 0, esirisc->ecas->size);
+       uint32_t eid = buf_get_u32(esirisc->eid->value, 0, esirisc->eid->size);
+       uint32_t ed = buf_get_u32(esirisc->ed->value, 0, esirisc->ed->size);
+
+       LOG_DEBUG("-");
+
+       const char *exception = "Unknown";
+       if (eid < ARRAY_SIZE(esirisc_exceptions))
+               exception = esirisc_exceptions[eid];
+
+       LOG_USER("target halted due to %s, exception: %s\n"
+                       "EPC: 0x%" PRIx32 " ECAS: 0x%" PRIx32 " EID: 0x%" PRIx32 " ED: 0x%" PRIx32,
+                       debug_reason_name(target), exception, epc, ecas, eid, ed);
+
+       return ERROR_OK;
+}
+
+static const char *esirisc_get_gdb_arch(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+
+       LOG_DEBUG("-");
+
+       /*
+        * Targets with the UNIFIED_ADDRESS_SPACE option disabled employ a
+        * Harvard architecture. This option is not exposed in a CSR, which
+        * requires additional configuration to properly interact with these
+        * targets in GDB (also see: `esirisc cache_arch`).
+        */
+       if (esirisc->gdb_arch == NULL && target_was_examined(target))
+               esirisc->gdb_arch = alloc_printf("esirisc:%d_bit_%d_reg_%s",
+                               esirisc->num_bits, esirisc->num_regs, esirisc_cache_arch(esirisc));
+
+       return esirisc->gdb_arch;
+}
+
+static int esirisc_get_gdb_reg_list(struct target *target, struct reg **reg_list[],
+               int *reg_list_size, enum target_register_class reg_class)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+
+       LOG_DEBUG("-");
+
+       *reg_list_size = ESIRISC_NUM_REGS;
+
+       *reg_list = calloc(*reg_list_size, sizeof(struct reg *));
+       if (*reg_list == NULL)
+               return ERROR_FAIL;
+
+       if (reg_class == REG_CLASS_ALL)
+               for (int i = 0; i < *reg_list_size; ++i)
+                       (*reg_list)[i] = esirisc->reg_cache->reg_list + i;
+       else {
+               for (int i = 0; i < esirisc->num_regs; ++i)
+                       (*reg_list)[i] = esirisc->reg_cache->reg_list + i;
+
+               (*reg_list)[ESIRISC_PC] = esirisc->reg_cache->reg_list + ESIRISC_PC;
+               (*reg_list)[ESIRISC_CAS] = esirisc->reg_cache->reg_list + ESIRISC_CAS;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_read_reg(struct reg *reg)
+{
+       struct esirisc_reg *reg_info = reg->arch_info;
+       struct esirisc_common *esirisc = reg_info->esirisc;
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       struct target *target = esirisc->target;
+       uint32_t data;
+
+       LOG_DEBUG("-");
+
+       int retval = esirisc_jtag_read_reg(jtag_info, reg->number, &data);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read register: %s", target_name(target), reg->name);
+               return retval;
+       }
+
+       buf_set_u32(reg->value, 0, reg->size, data);
+       reg->dirty = false;
+       reg->valid = true;
+
+       return ERROR_OK;
+}
+
+static int esirisc_write_reg(struct reg *reg)
+{
+       struct esirisc_reg *reg_info = reg->arch_info;
+       struct esirisc_common *esirisc = reg_info->esirisc;
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       struct target *target = esirisc->target;
+       uint32_t data = buf_get_u32(reg->value, 0, reg->size);
+
+       LOG_DEBUG("-");
+
+       int retval = esirisc_jtag_write_reg(jtag_info, reg->number, data);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write register: %s", target_name(target), reg->name);
+               return retval;
+       }
+
+       reg->dirty = false;
+       reg->valid = true;
+
+       return ERROR_OK;
+}
+
+static int esirisc_read_csr(struct reg *reg)
+{
+       struct esirisc_reg *reg_info = reg->arch_info;
+       struct esirisc_common *esirisc = reg_info->esirisc;
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       struct target *target = esirisc->target;
+       uint32_t data;
+
+       LOG_DEBUG("-");
+
+       int retval = esirisc_jtag_read_csr(jtag_info, reg_info->bank, reg_info->csr, &data);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: %s", target_name(target), reg->name);
+               return retval;
+       }
+
+       buf_set_u32(reg->value, 0, reg->size, data);
+       reg->dirty = false;
+       reg->valid = true;
+
+       return ERROR_OK;
+}
+
+static int esirisc_write_csr(struct reg *reg)
+{
+       struct esirisc_reg *reg_info = reg->arch_info;
+       struct esirisc_common *esirisc = reg_info->esirisc;
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       struct target *target = esirisc->target;
+       uint32_t data = buf_get_u32(reg->value, 0, reg->size);
+
+       LOG_DEBUG("-");
+
+       int retval = esirisc_jtag_write_csr(jtag_info, reg_info->bank, reg_info->csr, data);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to write CSR: %s", target_name(target), reg->name);
+               return retval;
+       }
+
+       reg->dirty = false;
+       reg->valid = true;
+
+       return ERROR_OK;
+}
+
+static int esirisc_get_reg(struct reg *reg)
+{
+       struct esirisc_reg *reg_info = reg->arch_info;
+       struct esirisc_common *esirisc = reg_info->esirisc;
+       struct target *target = esirisc->target;
+
+       LOG_DEBUG("-");
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       return reg_info->read(reg);
+}
+
+static int esirisc_set_reg(struct reg *reg, uint8_t *buf)
+{
+       struct esirisc_reg *reg_info = reg->arch_info;
+       struct esirisc_common *esirisc = reg_info->esirisc;
+       struct target *target = esirisc->target;
+       uint32_t value = buf_get_u32(buf, 0, reg->size);
+
+       LOG_DEBUG("-");
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       buf_set_u32(reg->value, 0, reg->size, value);
+       reg->dirty = true;
+       reg->valid = true;
+
+       return ERROR_OK;
+}
+
+static const struct reg_arch_type esirisc_reg_type = {
+       .get = esirisc_get_reg,
+       .set = esirisc_set_reg,
+};
+
+static struct reg_cache *esirisc_build_reg_cache(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache);
+       struct reg_cache *cache = malloc(sizeof(struct reg_cache));
+       struct reg *reg_list = calloc(ESIRISC_NUM_REGS, sizeof(struct reg));
+
+       LOG_DEBUG("-");
+
+       cache->name = "eSi-RISC registers";
+       cache->next = NULL;
+       cache->reg_list = reg_list;
+       cache->num_regs = ESIRISC_NUM_REGS;
+       (*cache_p) = cache;
+
+       esirisc->reg_cache = cache;
+       esirisc->epc = reg_list + ESIRISC_EPC;
+       esirisc->ecas = reg_list + ESIRISC_ECAS;
+       esirisc->eid = reg_list + ESIRISC_EID;
+       esirisc->ed = reg_list + ESIRISC_ED;
+
+       for (int i = 0; i < esirisc->num_regs; ++i) {
+               struct reg *reg = reg_list + esirisc_regs[i].number;
+               struct esirisc_reg *reg_info = calloc(1, sizeof(struct esirisc_reg));
+
+               reg->name = esirisc_regs[i].name;
+               reg->number = esirisc_regs[i].number;
+               reg->value = calloc(1, DIV_ROUND_UP(esirisc->num_bits, 8));
+               reg->size = esirisc->num_bits;
+               reg->reg_data_type = calloc(1, sizeof(struct reg_data_type));
+               reg->reg_data_type->type = esirisc_regs[i].type;
+               reg->group = esirisc_regs[i].group;
+               reg_info->esirisc = esirisc;
+               reg_info->read = esirisc_read_reg;
+               reg_info->write = esirisc_write_reg;
+               reg->arch_info = reg_info;
+               reg->type = &esirisc_reg_type;
+               reg->exist = true;
+       }
+
+       for (size_t i = 0; i < ARRAY_SIZE(esirisc_csrs); ++i) {
+               struct reg *reg = reg_list + esirisc_csrs[i].number;
+               struct esirisc_reg *reg_info = calloc(1, sizeof(struct esirisc_reg));
+
+               reg->name = esirisc_csrs[i].name;
+               reg->number = esirisc_csrs[i].number;
+               reg->value = calloc(1, DIV_ROUND_UP(esirisc->num_bits, 8));
+               reg->size = esirisc->num_bits;
+               reg->reg_data_type = calloc(1, sizeof(struct reg_data_type));
+               reg->reg_data_type->type = esirisc_csrs[i].type;
+               reg->group = esirisc_csrs[i].group;
+               reg_info->esirisc = esirisc;
+               reg_info->bank = esirisc_csrs[i].bank;
+               reg_info->csr = esirisc_csrs[i].csr;
+               reg_info->read = esirisc_read_csr;
+               reg_info->write = esirisc_write_csr;
+               reg->arch_info = reg_info;
+               reg->type = &esirisc_reg_type;
+               reg->exist = true;
+       }
+
+       return cache;
+}
+
+static int esirisc_identify(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       uint32_t csr;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_ARCH0, &csr);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: ARCH0", target_name(target));
+               return retval;
+       }
+
+       esirisc->num_bits = (csr >> 0) & 0x3f;                  /* ARCH0.B */
+       esirisc->num_regs = (csr >> 10) & 0x3f;                 /* ARCH0.R */
+
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_MEM, &csr);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: MEM", target_name(target));
+               return retval;
+       }
+
+       target->endianness = (csr & 1<<0) ?                             /* MEM.E */
+                       TARGET_BIG_ENDIAN : TARGET_LITTLE_ENDIAN;
+
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_IC, &csr);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: IC", target_name(target));
+               return retval;
+       }
+
+       esirisc->has_icache = !!(csr & 1<<0);                   /* IC.E */
+
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_DC, &csr);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: DC", target_name(target));
+               return retval;
+       }
+
+       esirisc->has_dcache = !!(csr & 1<<0);                   /* DC.E */
+
+       retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_DBG, &csr);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to read CSR: DBG", target_name(target));
+               return retval;
+       }
+
+       esirisc->num_breakpoints = (csr >> 7) & 0xf;    /* DBG.BP */
+       esirisc->num_watchpoints = (csr >> 12) & 0xf;   /* DBG.WP */
+
+       return ERROR_OK;
+}
+
+static int esirisc_target_create(struct target *target, Jim_Interp *interp)
+{
+       struct jtag_tap *tap = target->tap;
+       struct esirisc_common *esirisc;
+
+       if (tap == NULL)
+               return ERROR_FAIL;
+
+       if (tap->ir_length != INSTR_LENGTH) {
+               LOG_ERROR("%s: invalid IR length; expected %d", target_name(target),
+                               INSTR_LENGTH);
+               return ERROR_FAIL;
+       }
+
+       esirisc = calloc(1, sizeof(struct esirisc_common));
+       if (esirisc == NULL)
+               return ERROR_FAIL;
+
+       esirisc->target = target;
+       esirisc->jtag_info.tap = tap;
+       target->arch_info = esirisc;
+
+       return ERROR_OK;
+}
+
+static int esirisc_init_target(struct command_context *cmd_ctx, struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+
+       /* trap reset, error, and debug exceptions */
+       esirisc->hwdc_save = HWDC_R | HWDC_E | HWDC_D;
+
+       return ERROR_OK;
+}
+
+static int esirisc_examine(struct target *target)
+{
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       if (!target_was_examined(target)) {
+               retval = esirisc_debug_enable(target);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               /*
+                * In order to identify the target we must first halt the core.
+                * We quietly resume once identification has completed for those
+                * targets that were running when target_examine was called.
+                */
+               if (esirisc_jtag_is_debug_active(jtag_info)) {
+                       if (target->state == TARGET_UNKNOWN)
+                               target->debug_reason = DBG_REASON_DBGRQ;
+
+                       target->state = TARGET_HALTED;
+               } else {
+                       retval = esirisc_jtag_break(jtag_info);
+                       if (retval != ERROR_OK) {
+                               LOG_ERROR("%s: failed to halt target", target_name(target));
+                               return retval;
+                       }
+
+                       target->state = TARGET_RUNNING;
+               }
+
+               retval = esirisc_identify(target);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("%s: failed to identify target", target_name(target));
+                       return retval;
+               }
+
+               esirisc_build_reg_cache(target);
+
+               esirisc_remove_breakpoints(target);
+               esirisc_remove_watchpoints(target);
+
+               esirisc_disable_step(target);
+               esirisc_restore_hwdc(target);
+
+               if (target->state == TARGET_HALTED)
+                       esirisc_save_interrupts(target);
+               else {
+                       retval = esirisc_jtag_continue(jtag_info);
+                       if (retval != ERROR_OK) {
+                               LOG_ERROR("%s: failed to resume target", target_name(target));
+                               return retval;
+                       }
+               }
+
+               target_set_examined(target);
+
+               LOG_INFO("%s: %d bit, %d registers, %s%s%s", target_name(target),
+                                esirisc->num_bits, esirisc->num_regs,
+                                target_endianness(target),
+                                esirisc->has_icache ? ", icache" : "",
+                                esirisc->has_dcache ? ", dcache" : "");
+
+               LOG_INFO("%s: hardware has %d breakpoints, %d watchpoints", target_name(target),
+                                esirisc->num_breakpoints, esirisc->num_watchpoints);
+       }
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_esirisc_cache_arch_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+
+       if (CMD_ARGC > 0) {
+               if (strcmp(*CMD_ARGV, "harvard") == 0)
+                       esirisc->cache_arch = ESIRISC_CACHE_HARVARD;
+               else if (strcmp(*CMD_ARGV, "von_neumann") == 0)
+                       esirisc->cache_arch = ESIRISC_CACHE_VON_NEUMANN;
+               else {
+                       LOG_ERROR("invalid cache_arch: %s", *CMD_ARGV);
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+       }
+
+       command_print(CMD_CTX, "esirisc cache_arch %s", esirisc_cache_arch(esirisc));
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_esirisc_flush_caches_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+       int retval;
+
+       if (!esirisc_has_cache(esirisc)) {
+               LOG_ERROR("target does not support caching");
+               return ERROR_FAIL;
+       }
+
+       retval = esirisc_flush_caches(target);
+
+       command_print(CMD_CTX, "cache flush %s",
+                       (retval == ERROR_OK) ? "successful" : "failed");
+
+       return retval;
+}
+
+static const struct {
+       const char *name;
+       int mask;
+} esirisc_hwdc_masks[] = {
+       { "reset",              HWDC_R },
+       { "interrupt",  HWDC_I },
+       { "syscall",    HWDC_S },
+       { "error",              HWDC_E },
+       { "debug",              HWDC_D },
+};
+
+static int esirisc_find_hwdc_mask(const char *name)
+{
+       for (size_t i = 0; i < ARRAY_SIZE(esirisc_hwdc_masks); ++i)
+               if (strcmp(esirisc_hwdc_masks[i].name, name) == 0)
+                       return esirisc_hwdc_masks[i].mask;
+
+       return -1;
+}
+
+COMMAND_HANDLER(handle_esirisc_hwdc_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+
+       if (CMD_ARGC > 0) {
+               if (strcmp(CMD_ARGV[0], "all") == 0)
+                       esirisc->hwdc_save = HWDC_R | HWDC_I | HWDC_S | HWDC_E | HWDC_D;
+               else {
+                       esirisc->hwdc_save = 0;
+                       if (strcmp(CMD_ARGV[0], "none") != 0) {
+                               while (CMD_ARGC-- > 0) {
+                                       int mask = esirisc_find_hwdc_mask(CMD_ARGV[CMD_ARGC]);
+                                       if (mask < 0) {
+                                               LOG_ERROR("invalid mask: %s", CMD_ARGV[CMD_ARGC]);
+                                               return ERROR_COMMAND_SYNTAX_ERROR;
+                                       }
+                                       esirisc->hwdc_save |= mask;
+                               }
+                       }
+               }
+       }
+
+       for (size_t i = 0; i < ARRAY_SIZE(esirisc_hwdc_masks); ++i)
+               command_print(CMD_CTX, "%9s: %s", esirisc_hwdc_masks[i].name,
+                               (esirisc->hwdc_save & esirisc_hwdc_masks[i].mask) ? "enabled" : "disabled");
+
+       return ERROR_OK;
+}
+
+static const struct command_registration esirisc_exec_command_handlers[] = {
+       {
+               .name = "cache_arch",
+               .handler = handle_esirisc_cache_arch_command,
+               .mode = COMMAND_ANY,
+               .help = "configure cache architecture",
+               .usage = "['harvard'|'von_neumann']",
+       },
+       {
+               .name = "flush_caches",
+               .handler = handle_esirisc_flush_caches_command,
+               .mode = COMMAND_EXEC,
+               .help = "flush instruction and data caches",
+               .usage = "",
+       },
+       {
+               .name = "hwdc",
+               .handler = handle_esirisc_hwdc_command,
+               .mode = COMMAND_ANY,
+               .help = "configure hardware debug control",
+               .usage = "['all'|'none'|mask ...]",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration esirisc_command_handlers[] = {
+       {
+               .name = "esirisc",
+               .mode = COMMAND_ANY,
+               .help = "eSi-RISC command group",
+               .usage = "",
+               .chain = esirisc_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct target_type esirisc_target = {
+       .name = "esirisc",
+
+       .poll = esirisc_poll,
+       .arch_state = esirisc_arch_state,
+
+       .halt = esirisc_halt,
+       .resume = esirisc_resume,
+       .step = esirisc_step,
+
+       .assert_reset = esirisc_assert_reset,
+       .deassert_reset = esirisc_deassert_reset,
+
+       .get_gdb_arch = esirisc_get_gdb_arch,
+       .get_gdb_reg_list = esirisc_get_gdb_reg_list,
+
+       .read_memory = esirisc_read_memory,
+       .write_memory = esirisc_write_memory,
+       .checksum_memory = esirisc_checksum_memory,
+
+       .add_breakpoint = esirisc_add_breakpoint,
+       .remove_breakpoint = esirisc_remove_breakpoint,
+       .add_watchpoint = esirisc_add_watchpoint,
+       .remove_watchpoint = esirisc_remove_watchpoint,
+
+       .commands = esirisc_command_handlers,
+
+       .target_create = esirisc_target_create,
+       .init_target = esirisc_init_target,
+       .examine = esirisc_examine,
+};