esirisc: support eSi-RISC targets 60/4660/17
authorSteven Stallion <stallion@squareup.com>
Wed, 29 Aug 2018 00:18:01 +0000 (17:18 -0700)
committerMatthias Welwarsky <matthias@welwarsky.de>
Tue, 16 Oct 2018 10:58:24 +0000 (11:58 +0100)
eSi-RISC is a highly configurable microprocessor architecture for
embedded systems provided by EnSilica. This patch adds support for
32-bit targets and also includes an internal flash driver and
uC/OS-III RTOS support. This is a non-traditional target and required
a number of additional changes to support non-linear register numbers
and the 'p' packet in RTOS support for proper integration into
EnSilica's GDB port.

Change-Id: I59d5c40b3bb2ace1b1a01b2538bfab211adf113f
Signed-off-by: Steven Stallion <stallion@squareup.com>
Reviewed-on: http://openocd.zylin.com/4660
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
17 files changed:
README
doc/openocd.texi
src/flash/nor/Makefile.am
src/flash/nor/drivers.c
src/flash/nor/esirisc_flash.c [new file with mode: 0644]
src/rtos/rtos_ucos_iii_stackings.c
src/rtos/rtos_ucos_iii_stackings.h
src/rtos/uCOS-III.c
src/target/Makefile.am
src/target/esirisc.c [new file with mode: 0644]
src/target/esirisc.h [new file with mode: 0644]
src/target/esirisc_jtag.c [new file with mode: 0644]
src/target/esirisc_jtag.h [new file with mode: 0644]
src/target/esirisc_regs.h [new file with mode: 0644]
src/target/target.c
src/target/target.h
tcl/target/esi32xx.cfg [new file with mode: 0644]

diff --git a/README b/README
index 985e39a9826cb588adfb96c16c03a2e22f69f0ad..e1e1b94d3d1a6b521dd9498b63cb1b0680b654cb 100644 (file)
--- a/README
+++ b/README
@@ -117,17 +117,18 @@ Debug targets
 -------------
 
 ARM11, ARM7, ARM9, AVR32, Cortex-A, Cortex-R, Cortex-M, LS102x-SAP,
-Feroceon/Dragonite, DSP563xx, DSP5680xx, FA526, MIPS EJTAG, NDS32,
-XScale, Intel Quark.
+Feroceon/Dragonite, DSP563xx, DSP5680xx, EnSilica eSi-RISC, FA526, MIPS
+EJTAG, NDS32, XScale, Intel Quark.
 
 Flash drivers
 -------------
 
-ADUC702x, AT91SAM, ATH79, AVR, CFI, DSP5680xx, EFM32, EM357, FM3, FM4, Kinetis,
-LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900, LPCSPIFI, Marvell QSPI,
-Milandr, NIIET, NuMicro, PIC32mx, PSoC4, PSoC5LP, SiM3x, Stellaris, STM32,
-STMSMI, STR7x, STR9x, nRF51; NAND controllers of AT91SAM9, LPC3180, LPC32xx,
-i.MX31, MXC, NUC910, Orion/Kirkwood, S3C24xx, S3C6400, XMC1xxx, XMC4xxx.
+ADUC702x, AT91SAM, ATH79, AVR, CFI, DSP5680xx, EFM32, EM357, eSi-TSMC,
+FM3, FM4, Kinetis, LPC8xx/LPC1xxx/LPC2xxx/LPC541xx, LPC2900, LPCSPIFI,
+Marvell QSPI, Milandr, NIIET, NuMicro, PIC32mx, PSoC4, PSoC5LP, SiM3x,
+Stellaris, STM32, STMSMI, STR7x, STR9x, nRF51; NAND controllers of
+AT91SAM9, LPC3180, LPC32xx, i.MX31, MXC, NUC910, Orion/Kirkwood,
+S3C24xx, S3C6400, XMC1xxx, XMC4xxx.
 
 
 ==================
index bbe6cffd4df533965894b75b7bd65f76f2efeef2..77c9ff7b6d75bdd50f0e36cff07f13cb1377c387 100644 (file)
@@ -4275,6 +4275,8 @@ compact Thumb2 instruction set.
 @item @code{dragonite} -- resembles arm966e
 @item @code{dsp563xx} -- implements Freescale's 24-bit DSP.
 (Support for this is still incomplete.)
+@item @code{esirisc} -- this is an EnSilica eSi-RISC core.
+The current implementation supports eSi-32xx cores.
 @item @code{fa526} -- resembles arm920 (w/o Thumb)
 @item @code{feroceon} -- resembles arm926
 @item @code{mips_m4k} -- a MIPS core
@@ -5647,6 +5649,27 @@ Note that in order for this command to take effect, the target needs to be reset
 supported.}
 @end deffn
 
+@deffn {Flash Driver} esirisc
+Members of the eSi-RISC family may optionally include internal flash programmed
+via the eSi-TSMC Flash interface. Additional parameters are required to
+configure the driver: @option{cfg_address} is the base address of the
+configuration register interface, @option{clock_hz} is the expected clock
+frequency, and @option{wait_states} is the number of configured read wait states.
+
+@example
+flash bank $_FLASHNAME esirisc base_address size_bytes 0 0 $_TARGETNAME cfg_address clock_hz wait_states
+@end example
+
+@deffn Command {esirisc_flash mass_erase} (bank_id)
+Erases all pages in data memory for the bank identified by @option{bank_id}.
+@end deffn
+
+@deffn Command {esirisc_flash ref_erase} (bank_id)
+Erases the reference cell for the bank identified by @option{bank_id}. This is
+an uncommon operation.
+@end deffn
+@end deffn
+
 @deffn {Flash Driver} fm3
 All members of the FM3 microcontroller family from Fujitsu
 include internal flash and use ARM Cortex-M3 cores.
@@ -8933,6 +8956,29 @@ Selects whether interrupts will be processed when single stepping. The default c
 @option{on}.
 @end deffn
 
+@section EnSilica eSi-RISC Architecture
+
+eSi-RISC is a highly configurable microprocessor architecture for embedded systems
+provided by EnSilica. (See: @url{http://www.ensilica.com/risc-ip/}.)
+
+@subsection esirisc specific commands
+@deffn Command {esirisc cache_arch} (@option{harvard}|@option{von_neumann})
+Configure the caching architecture. Targets with the @code{UNIFIED_ADDRESS_SPACE}
+option disabled employ a Harvard architecture. By default, @option{von_neumann} is assumed.
+@end deffn
+
+@deffn Command {esirisc flush_caches}
+Flush instruction and data caches. This command requires that the target is halted
+when the command is issued and configured with an instruction or data cache.
+@end deffn
+
+@deffn Command {esirisc hwdc} (@option{all}|@option{none}|mask ...)
+Configure hardware debug control. The HWDC register controls which exceptions return
+control back to the debugger. Possible masks are @option{all}, @option{none},
+@option{reset}, @option{interrupt}, @option{syscall}, @option{error}, and @option{debug}.
+By default, @option{reset}, @option{error}, and @option{debug} are enabled.
+@end deffn
+
 @section Intel Architecture
 
 Intel Quark X10xx is the first product in the Quark family of SoCs. It is an IA-32
index 3839d0a8dd9cccc125d007963ddf3941d613bcc5..864f7f29a5fa23acadffdb2c5dca66a6dc6e9b7f 100644 (file)
@@ -25,6 +25,7 @@ NOR_DRIVERS = \
        %D%/dsp5680xx_flash.c \
        %D%/efm32.c \
        %D%/em357.c \
+       %D%/esirisc_flash.c \
        %D%/faux.c \
        %D%/fm3.c \
        %D%/fm4.c \
index 2251b965a6c9fb6fda592151c426343c7a43e308..4ffd5aca5be5c1de28f8848385a8884bd2a0d072 100644 (file)
@@ -38,6 +38,7 @@ extern struct flash_driver cfi_flash;
 extern struct flash_driver dsp5680xx_flash;
 extern struct flash_driver efm32_flash;
 extern struct flash_driver em357_flash;
+extern struct flash_driver esirisc_flash;
 extern struct flash_driver faux_flash;
 extern struct flash_driver fm3_flash;
 extern struct flash_driver fm4_flash;
@@ -103,6 +104,7 @@ static struct flash_driver *flash_drivers[] = {
        &dsp5680xx_flash,
        &efm32_flash,
        &em357_flash,
+       &esirisc_flash,
        &faux_flash,
        &fm3_flash,
        &fm4_flash,
diff --git a/src/flash/nor/esirisc_flash.c b/src/flash/nor/esirisc_flash.c
new file mode 100644 (file)
index 0000000..f3833df
--- /dev/null
@@ -0,0 +1,621 @@
+/***************************************************************************
+ *   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 <flash/common.h>
+#include <flash/nor/imp.h>
+#include <helper/command.h>
+#include <helper/log.h>
+#include <helper/time_support.h>
+#include <helper/types.h>
+#include <target/esirisc.h>
+#include <target/target.h>
+
+/* eSi-TSMC Flash Registers */
+#define CONTROL                                0x00    /* Control Register */
+#define TIMING0                                0x04    /* Timing Register 0 */
+#define TIMING1                                0x08    /* Timing Register 1 */
+#define TIMING2                                0x0c    /* Timing Register 2 */
+#define UNLOCK1                                0x18    /* Unlock 1 */
+#define UNLOCK2                                0x1c    /* Unlock 2 */
+#define ADDRESS                                0x20    /* Erase/Program Address */
+#define PB_DATA                                0x24    /* Program Buffer Data */
+#define PB_INDEX                       0x28    /* Program Buffer Index */
+#define STATUS                         0x2c    /* Status Register */
+#define REDUN_0                                0x30    /* Redundant Address 0 */
+#define REDUN_1                                0x34    /* Redundant Address 1 */
+
+/* Control Fields */
+#define CONTROL_SLM                    (1<<0)  /* Sleep Mode */
+#define CONTROL_WP                     (1<<1)  /* Register Write Protect */
+#define CONTROL_E                      (1<<3)  /* Erase */
+#define CONTROL_EP                     (1<<4)  /* Erase Page */
+#define CONTROL_P                      (1<<5)  /* Program Flash */
+#define CONTROL_ERC                    (1<<6)  /* Erase Reference Cell */
+#define CONTROL_R                      (1<<7)  /* Recall Trim Code */
+#define CONTROL_AP                     (1<<8)  /* Auto-Program */
+
+/* Timing Fields */
+#define TIMING0_R(x)           (((x) <<  0) & 0x3f)            /* Read Wait States */
+#define TIMING0_F(x)           (((x) << 16) & 0xffff0000)      /* Tnvh Clock Cycles */
+#define TIMING1_E(x)           (((x) <<  0) & 0xffffff)        /* Tme/Terase/Tre Clock Cycles */
+#define TIMING2_P(x)           (((x) <<  0) & 0xffff)          /* Tprog Clock Cycles */
+#define TIMING2_H(x)           (((x) << 16) & 0xff0000)        /* Clock Cycles in 100ns */
+#define TIMING2_T(x)           (((x) << 24) & 0xf000000)       /* Clock Cycles in 10ns */
+
+/* Status Fields */
+#define STATUS_BUSY                    (1<<0)  /* Busy (Erase/Program) */
+#define STATUS_WER                     (1<<1)  /* Write Protect Error */
+#define STATUS_DR                      (1<<2)  /* Disable Redundancy */
+#define STATUS_DIS                     (1<<3)  /* Discharged */
+#define STATUS_BO                      (1<<4)  /* Brown Out */
+
+/* Redundant Address Fields */
+#define REDUN_R                                (1<<0)                                          /* Used */
+#define REDUN_P(x)                     (((x) << 12) & 0x7f000)         /* Redundant Page Address */
+
+/*
+ * The eSi-TSMC Flash manual provides two sets of timings based on the
+ * underlying flash process. By default, 90nm is assumed.
+ */
+#if 0 /* 55nm */
+#define TNVH                           5000            /* 5us   */
+#define TME                                    80000000        /* 80ms  */
+#define TERASE                         160000000       /* 160ms */
+#define TRE                                    100000000       /* 100ms */
+#define TPROG                          8000            /* 8us   */
+#else /* 90nm */
+#define TNVH                           5000            /* 5us   */
+#define TME                                    20000000        /* 20ms  */
+#define TERASE                         40000000        /* 40ms  */
+#define TRE                                    40000000        /* 40ms  */
+#define TPROG                          40000           /* 40us  */
+#endif
+
+#define CONTROL_TIMEOUT                5000            /* 5s    */
+#define PAGE_SIZE                      4096
+#define PB_MAX                         32
+
+#define NUM_NS_PER_S           1000000000ULL
+
+struct esirisc_flash_bank {
+       bool probed;
+       uint32_t cfg;
+       uint32_t clock;
+       uint32_t wait_states;
+};
+
+FLASH_BANK_COMMAND_HANDLER(esirisc_flash_bank_command)
+{
+       struct esirisc_flash_bank *esirisc_info;
+
+       if (CMD_ARGC < 9)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       esirisc_info = calloc(1, sizeof(struct esirisc_flash_bank));
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], esirisc_info->cfg);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], esirisc_info->clock);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[8], esirisc_info->wait_states);
+
+       bank->driver_priv = esirisc_info;
+
+       return ERROR_OK;
+}
+
+/*
+ * Register writes are ignored if the control.WP flag is set; the
+ * following sequence is required to modify this flag even when
+ * protection is disabled.
+ */
+static int esirisc_flash_unlock(struct flash_bank *bank)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+
+       target_write_u32(target, esirisc_info->cfg + UNLOCK1, 0x7123);
+       target_write_u32(target, esirisc_info->cfg + UNLOCK2, 0x812a);
+       target_write_u32(target, esirisc_info->cfg + UNLOCK1, 0xbee1);
+
+       return ERROR_OK;
+}
+
+static int esirisc_flash_disable_protect(struct flash_bank *bank)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+       uint32_t control;
+
+       target_read_u32(target, esirisc_info->cfg + CONTROL, &control);
+       if (!(control & CONTROL_WP))
+               return ERROR_OK;
+
+       esirisc_flash_unlock(bank);
+
+       control &= ~CONTROL_WP;
+
+       target_write_u32(target, esirisc_info->cfg + CONTROL, control);
+
+       return ERROR_OK;
+}
+
+static int esirisc_flash_enable_protect(struct flash_bank *bank)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+       uint32_t control;
+
+       target_read_u32(target, esirisc_info->cfg + CONTROL, &control);
+       if (control & CONTROL_WP)
+               return ERROR_OK;
+
+       esirisc_flash_unlock(bank);
+
+       control |= CONTROL_WP;
+
+       target_write_u32(target, esirisc_info->cfg + CONTROL, control);
+
+       return ERROR_OK;
+}
+
+static int esirisc_flash_check_status(struct flash_bank *bank)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+       uint32_t status;
+
+       target_read_u32(target, esirisc_info->cfg + STATUS, &status);
+       if (status & STATUS_WER) {
+               LOG_ERROR("%s: bad status: 0x%" PRIx32, bank->name, status);
+               return ERROR_FLASH_OPERATION_FAILED;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_flash_clear_status(struct flash_bank *bank)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+
+       target_write_u32(target, esirisc_info->cfg + STATUS, STATUS_WER);
+
+       return ERROR_OK;
+}
+
+static int esirisc_flash_wait(struct flash_bank *bank, int ms)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+       uint32_t status;
+       int64_t t;
+
+       t = timeval_ms();
+       for (;;) {
+               target_read_u32(target, esirisc_info->cfg + STATUS, &status);
+               if (!(status & STATUS_BUSY))
+                       return ERROR_OK;
+
+               if ((timeval_ms() - t) > ms)
+                       return ERROR_TARGET_TIMEOUT;
+
+               keep_alive();
+       }
+}
+
+static int esirisc_flash_control(struct flash_bank *bank, uint32_t control)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+
+       esirisc_flash_clear_status(bank);
+
+       target_write_u32(target, esirisc_info->cfg + CONTROL, control);
+
+       int retval = esirisc_flash_wait(bank, CONTROL_TIMEOUT);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: control timed out: 0x%" PRIx32, bank->name, control);
+               return retval;
+       }
+
+       return esirisc_flash_check_status(bank);
+}
+
+static int esirisc_flash_recall(struct flash_bank *bank)
+{
+       return esirisc_flash_control(bank, CONTROL_R);
+}
+
+static int esirisc_flash_erase(struct flash_bank *bank, int first, int last)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+       int retval = ERROR_OK;
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       esirisc_flash_disable_protect(bank);
+
+       for (int page = first; page < last; ++page) {
+               uint32_t address = page * PAGE_SIZE;
+
+               target_write_u32(target, esirisc_info->cfg + ADDRESS, address);
+
+               retval = esirisc_flash_control(bank, CONTROL_EP);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("%s: failed to erase address: 0x%" PRIx32, bank->name, address);
+                       break;
+               }
+       }
+
+       esirisc_flash_enable_protect(bank);
+
+       return retval;
+}
+
+static int esirisc_flash_mass_erase(struct flash_bank *bank)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+       int retval;
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       esirisc_flash_disable_protect(bank);
+
+       target_write_u32(target, esirisc_info->cfg + ADDRESS, 0);
+
+       retval = esirisc_flash_control(bank, CONTROL_E);
+       if (retval != ERROR_OK)
+               LOG_ERROR("%s: failed to mass erase", bank->name);
+
+       esirisc_flash_enable_protect(bank);
+
+       return retval;
+}
+
+/*
+ * Per TSMC, the reference cell should be erased once per sample. This
+ * is typically done during wafer sort, however we include support for
+ * those that may need to calibrate flash at a later time.
+ */
+static int esirisc_flash_ref_erase(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       int retval;
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       esirisc_flash_disable_protect(bank);
+
+       retval = esirisc_flash_control(bank, CONTROL_ERC);
+       if (retval != ERROR_OK)
+               LOG_ERROR("%s: failed to erase reference cell", bank->name);
+
+       esirisc_flash_enable_protect(bank);
+
+       return retval;
+}
+
+static int esirisc_flash_protect(struct flash_bank *bank, int set, int first, int last)
+{
+       struct target *target = bank->target;
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       if (set)
+               esirisc_flash_enable_protect(bank);
+       else
+               esirisc_flash_disable_protect(bank);
+
+       return ERROR_OK;
+}
+
+static int esirisc_flash_fill_pb(struct flash_bank *bank,
+               const uint8_t *buffer, uint32_t count)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+       struct esirisc_common *esirisc = target_to_esirisc(target);
+
+       /*
+        * The pb_index register is auto-incremented when pb_data is written
+        * and should be cleared before each operation.
+        */
+       target_write_u32(target, esirisc_info->cfg + PB_INDEX, 0);
+
+       /*
+        * The width of the pb_data register depends on the underlying
+        * target; writing one byte at a time incurs a significant
+        * performance penalty and should be avoided.
+        */
+       while (count > 0) {
+               uint32_t max_bytes = DIV_ROUND_UP(esirisc->num_bits, 8);
+               uint32_t num_bytes = MIN(count, max_bytes);
+
+               target_write_buffer(target, esirisc_info->cfg + PB_DATA, num_bytes, buffer);
+
+               buffer += num_bytes;
+               count -= num_bytes;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_flash_write(struct flash_bank *bank,
+               const uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+       int retval = ERROR_OK;
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       esirisc_flash_disable_protect(bank);
+
+       /*
+        * The address register is auto-incremented based on the contents of
+        * the pb_index register after each operation completes. It can be
+        * set once provided pb_index is cleared before each operation.
+        */
+       target_write_u32(target, esirisc_info->cfg + ADDRESS, offset);
+
+       /*
+        * Care must be taken when filling the program buffer; a maximum of
+        * 32 bytes may be written at a time and may not cross a 32-byte
+        * boundary based on the current offset.
+        */
+       while (count > 0) {
+               uint32_t max_bytes = PB_MAX - (offset & 0x1f);
+               uint32_t num_bytes = MIN(count, max_bytes);
+
+               esirisc_flash_fill_pb(bank, buffer, num_bytes);
+
+               retval = esirisc_flash_control(bank, CONTROL_P);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("%s: failed to program address: 0x%" PRIx32, bank->name, offset);
+                       break;
+               }
+
+               buffer += num_bytes;
+               offset += num_bytes;
+               count -= num_bytes;
+       }
+
+       esirisc_flash_enable_protect(bank);
+
+       return retval;
+}
+
+static uint32_t esirisc_flash_num_cycles(struct flash_bank *bank, uint64_t ns)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+
+       /* apply scaling factor to avoid truncation */
+       uint64_t hz = (uint64_t)esirisc_info->clock * 1000;
+       uint64_t num_cycles = ((hz / NUM_NS_PER_S) * ns) / 1000;
+
+       if (hz % NUM_NS_PER_S > 0)
+               num_cycles++;
+
+       return num_cycles;
+}
+
+static int esirisc_flash_init(struct flash_bank *bank)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+       uint32_t value;
+       int retval;
+
+       esirisc_flash_disable_protect(bank);
+
+       /* initialize timing registers */
+       value = TIMING0_F(esirisc_flash_num_cycles(bank, TNVH)) |
+                       TIMING0_R(esirisc_info->wait_states);
+
+       LOG_DEBUG("TIMING0: 0x%" PRIx32, value);
+       target_write_u32(target, esirisc_info->cfg + TIMING0, value);
+
+       value = TIMING1_E(esirisc_flash_num_cycles(bank, TERASE));
+
+       LOG_DEBUG("TIMING1: 0x%" PRIx32, value);
+       target_write_u32(target, esirisc_info->cfg + TIMING1, value);
+
+       value = TIMING2_T(esirisc_flash_num_cycles(bank, 10))   |
+                       TIMING2_H(esirisc_flash_num_cycles(bank, 100))  |
+                       TIMING2_P(esirisc_flash_num_cycles(bank, TPROG));
+
+       LOG_DEBUG("TIMING2: 0x%" PRIx32, value);
+       target_write_u32(target, esirisc_info->cfg + TIMING2, value);
+
+       /* recall trim code */
+       retval = esirisc_flash_recall(bank);
+       if (retval != ERROR_OK)
+               LOG_ERROR("%s: failed to recall trim code", bank->name);
+
+       esirisc_flash_enable_protect(bank);
+
+       return retval;
+}
+
+static int esirisc_flash_probe(struct flash_bank *bank)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+       int retval;
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       bank->num_sectors = bank->size / PAGE_SIZE;
+       bank->sectors = alloc_block_array(0, PAGE_SIZE, bank->num_sectors);
+
+       /*
+        * Register write protection is enforced using a single protection
+        * block for the entire bank. This is as good as it gets.
+        */
+       bank->num_prot_blocks = 1;
+       bank->prot_blocks = alloc_block_array(0, bank->size, bank->num_prot_blocks);
+
+       retval = esirisc_flash_init(bank);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("%s: failed to initialize bank", bank->name);
+               return retval;
+       }
+
+       esirisc_info->probed = true;
+
+       return ERROR_OK;
+}
+
+static int esirisc_flash_auto_probe(struct flash_bank *bank)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+
+       if (esirisc_info->probed)
+               return ERROR_OK;
+
+       return esirisc_flash_probe(bank);
+}
+
+static int esirisc_flash_protect_check(struct flash_bank *bank)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+       struct target *target = bank->target;
+       uint32_t control;
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       target_read_u32(target, esirisc_info->cfg + CONTROL, &control);
+
+       /* single protection block (also see: esirisc_flash_probe()) */
+       bank->prot_blocks[0].is_protected = !!(control & CONTROL_WP);
+
+       return ERROR_OK;
+}
+
+static int esirisc_flash_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+       struct esirisc_flash_bank *esirisc_info = bank->driver_priv;
+
+       snprintf(buf, buf_size,
+                       "%4s cfg at 0x%" PRIx32 ", clock %" PRId32 ", wait_states %" PRId32,
+                       "",     /* align with first line */
+                       esirisc_info->cfg,
+                       esirisc_info->clock,
+                       esirisc_info->wait_states);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_esirisc_flash_mass_erase_command)
+{
+       struct flash_bank *bank;
+       int retval;
+
+       if (CMD_ARGC < 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = esirisc_flash_mass_erase(bank);
+
+       command_print(CMD_CTX, "mass erase %s",
+                       (retval == ERROR_OK) ? "successful" : "failed");
+
+       return retval;
+}
+
+COMMAND_HANDLER(handle_esirisc_flash_ref_erase_command)
+{
+       struct flash_bank *bank;
+       int retval;
+
+       if (CMD_ARGC < 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = esirisc_flash_ref_erase(bank);
+
+       command_print(CMD_CTX, "erase reference cell %s",
+                       (retval == ERROR_OK) ? "successful" : "failed");
+
+       return retval;
+}
+
+static const struct command_registration esirisc_flash_exec_command_handlers[] = {
+       {
+               .name = "mass_erase",
+               .handler = handle_esirisc_flash_mass_erase_command,
+               .mode = COMMAND_EXEC,
+               .help = "erases all pages in data memory",
+               .usage = "bank_id",
+       },
+       {
+               .name = "ref_erase",
+               .handler = handle_esirisc_flash_ref_erase_command,
+               .mode = COMMAND_EXEC,
+               .help = "erases reference cell (uncommon)",
+               .usage = "bank_id",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration esirisc_flash_command_handlers[] = {
+       {
+               .name = "esirisc_flash",
+               .mode = COMMAND_ANY,
+               .help = "eSi-RISC flash command group",
+               .usage = "",
+               .chain = esirisc_flash_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver esirisc_flash = {
+       .name = "esirisc",
+       .commands = esirisc_flash_command_handlers,
+       .usage = "flash bank bank_id 'esirisc' base_address size_bytes 0 0 target "
+                       "cfg_address clock_hz wait_states",
+       .flash_bank_command = esirisc_flash_bank_command,
+       .erase = esirisc_flash_erase,
+       .protect = esirisc_flash_protect,
+       .write = esirisc_flash_write,
+       .read = default_flash_read,
+       .probe = esirisc_flash_probe,
+       .auto_probe = esirisc_flash_auto_probe,
+       .erase_check = default_flash_blank_check,
+       .protect_check = esirisc_flash_protect_check,
+       .info = esirisc_flash_info,
+};
index 0a7411eddb23f66568eba646ce5cb75e1a695b63..d093563bae899e8ee1a9b8ae48919651f855ad88 100644 (file)
@@ -24,6 +24,7 @@
 #include <rtos/rtos.h>
 #include <rtos/rtos_standard_stackings.h>
 #include <target/armv7m.h>
+#include <target/esirisc.h>
 
 static const struct stack_register_offset rtos_uCOS_III_Cortex_M_stack_offsets[] = {
        { ARMV7M_R0,   0x20, 32 },      /* r0   */
@@ -45,6 +46,27 @@ static const struct stack_register_offset rtos_uCOS_III_Cortex_M_stack_offsets[]
        { ARMV7M_xPSR, 0x3c, 32 },      /* xPSR */
 };
 
+static const struct stack_register_offset rtos_uCOS_III_eSi_RISC_stack_offsets[] = {
+       { ESIRISC_SP,  -2,   32 },      /* sp   */
+       { ESIRISC_RA,  0x48, 32 },      /* ra   */
+       { ESIRISC_R2,  0x44, 32 },      /* r2   */
+       { ESIRISC_R3,  0x40, 32 },      /* r3   */
+       { ESIRISC_R4,  0x3c, 32 },      /* r4   */
+       { ESIRISC_R5,  0x38, 32 },      /* r5   */
+       { ESIRISC_R6,  0x34, 32 },      /* r6   */
+       { ESIRISC_R7,  0x30, 32 },      /* r7   */
+       { ESIRISC_R8,  0x2c, 32 },      /* r8   */
+       { ESIRISC_R9,  0x28, 32 },      /* r9   */
+       { ESIRISC_R10, 0x24, 32 },      /* r10  */
+       { ESIRISC_R11, 0x20, 32 },      /* r11  */
+       { ESIRISC_R12, 0x1c, 32 },      /* r12  */
+       { ESIRISC_R13, 0x18, 32 },      /* r13  */
+       { ESIRISC_R14, 0x14, 32 },      /* r14  */
+       { ESIRISC_R15, 0x10, 32 },      /* r15  */
+       { ESIRISC_PC,  0x04, 32 },      /* PC   */
+       { ESIRISC_CAS, 0x08, 32 },      /* CAS  */
+};
+
 const struct rtos_register_stacking rtos_uCOS_III_Cortex_M_stacking = {
        0x40,                                                                                           /* stack_registers_size */
        -1,                                                                                                     /* stack_growth_direction */
@@ -52,3 +74,11 @@ const struct rtos_register_stacking rtos_uCOS_III_Cortex_M_stacking = {
        rtos_generic_stack_align8,                                                      /* stack_alignment */
        rtos_uCOS_III_Cortex_M_stack_offsets                            /* register_offsets */
 };
+
+const struct rtos_register_stacking rtos_uCOS_III_eSi_RISC_stacking = {
+       0x4c,                                                                                           /* stack_registers_size */
+       -1,                                                                                                     /* stack_growth_direction */
+       ARRAY_SIZE(rtos_uCOS_III_eSi_RISC_stack_offsets),       /* num_output_registers */
+       NULL,                                                                                           /* stack_alignment */
+       rtos_uCOS_III_eSi_RISC_stack_offsets                            /* register_offsets */
+};
index f4703da3798294eead7eb6ac81e18bc863e884cb..a9398138b5814883996f83c8ea5511f5e133a203 100644 (file)
@@ -26,5 +26,6 @@
 #include <rtos/rtos.h>
 
 extern const struct rtos_register_stacking rtos_uCOS_III_Cortex_M_stacking;
+extern const struct rtos_register_stacking rtos_uCOS_III_eSi_RISC_stacking;
 
 #endif /* OPENOCD_RTOS_RTOS_UCOS_III_STACKINGS_H */
index e06bf41f81c7d89dfb4af5b9215a60bdec36c662..3cd9c2ae6acf2bd0a7a46253e476dd4a3d443de9 100644 (file)
@@ -68,6 +68,20 @@ static const struct uCOS_III_params uCOS_III_params_list[] = {
                &rtos_uCOS_III_Cortex_M_stacking,       /* stacking_info */
                0,                                                                      /* num_threads */
        },
+       {
+               "esirisc",                                                      /* target_name */
+               sizeof(uint32_t),                                       /* pointer_width */
+               0,                                                                      /* thread_stack_offset */
+               0,                                                                      /* thread_name_offset */
+               0,                                                                      /* thread_state_offset */
+               0,                                                                      /* thread_priority_offset */
+               0,                                                                      /* thread_prev_offset */
+               0,                                                                      /* thread_next_offset */
+               false,                                                          /* thread_offsets_updated */
+               1,                                                                      /* threadid_start */
+               &rtos_uCOS_III_eSi_RISC_stacking,       /* stacking_info */
+               0,                                                                      /* num_threads */
+       },
 };
 
 static const char * const uCOS_III_symbol_list[] = {
index 4b7c8c076c15d9b83edf3384204381fc774482ca..05f174870015938ee355d0c94ccf83c1f2d20c87 100644 (file)
@@ -23,6 +23,7 @@ noinst_LTLIBRARIES += %D%/libtarget.la
        $(NDS32_SRC) \
        $(STM8_SRC) \
        $(INTEL_IA32_SRC) \
+       $(ESIRISC_SRC) \
        %D%/avrt.c \
        %D%/dsp563xx.c \
        %D%/dsp563xx_once.c \
@@ -139,6 +140,10 @@ INTEL_IA32_SRC = \
        %D%/lakemont.c \
        %D%/x86_32_common.c
 
+ESIRISC_SRC = \
+       %D%/esirisc.c \
+       %D%/esirisc_jtag.c
+
 %C%_libtarget_la_SOURCES += \
        %D%/algorithm.h \
        %D%/arm.h \
@@ -218,7 +223,10 @@ INTEL_IA32_SRC = \
        %D%/stm8.h \
        %D%/lakemont.h \
        %D%/x86_32_common.h \
-       %D%/arm_cti.h
+       %D%/arm_cti.h \
+       %D%/esirisc.h \
+       %D%/esirisc_jtag.h \
+       %D%/esirisc_regs.h
 
 include %D%/openrisc/Makefile.am
 include %D%/riscv/Makefile.am
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,
+};
diff --git a/src/target/esirisc.h b/src/target/esirisc.h
new file mode 100644 (file)
index 0000000..bb50652
--- /dev/null
@@ -0,0 +1,129 @@
+/***************************************************************************
+ *   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/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ESIRISC_H
+#define OPENOCD_TARGET_ESIRISC_H
+
+#include <target/breakpoints.h>
+#include <target/register.h>
+#include <target/target.h>
+
+#include "esirisc_jtag.h"
+#include "esirisc_regs.h"
+
+#define MAX_BREAKPOINTS                        8
+#define MAX_WATCHPOINTS                        8
+
+/* Exception IDs */
+#define EID_RESET                              0x00
+#define EID_HARDWARE_FAILURE   0x01
+#define EID_NMI                                        0x02
+#define EID_INST_BREAKPOINT            0x03
+#define EID_DATA_BREAKPOINT            0x04
+#define EID_UNSUPPORTED                        0x05
+#define EID_PRIVILEGE_VIOLATION        0x06
+#define EID_INST_BUS_ERROR             0x07
+#define EID_DATA_BUS_ERROR             0x08
+#define EID_ALIGNMENT_ERROR            0x09
+#define EID_ARITHMETIC_ERROR   0x0a
+#define EID_SYSTEM_CALL                        0x0b
+#define EID_MEMORY_MANAGEMENT  0x0c
+#define EID_UNRECOVERABLE              0x0d
+#define EID_INTERRUPTn                 0x20
+
+/* Exception Entry Points */
+#define ENTRY_RESET                            0x00
+#define ENTRY_UNRECOVERABLE            0x01
+#define ENTRY_HARDWARE_FAILURE 0x02
+#define ENTRY_RUNTIME                  0x03
+#define ENTRY_MEMORY                   0x04
+#define ENTRY_SYSCALL                  0x05
+#define ENTRY_DEBUG                            0x06
+#define ENTRY_NMI                              0x07
+#define ENTRY_INTERRUPTn               0x08
+
+/* Hardware Debug Control */
+#define HWDC_R                                 (1<<4)  /* Reset & Hardware Failure */
+#define HWDC_I                                 (1<<3)  /* Interrupts */
+#define HWDC_S                                 (1<<2)  /* System Calls */
+#define HWDC_E                                 (1<<1)  /* Program Errors */
+#define HWDC_D                                 (1<<0)  /* Debug Exceptions */
+
+enum esirisc_cache {
+       ESIRISC_CACHE_VON_NEUMANN,
+       ESIRISC_CACHE_HARVARD,
+};
+
+struct esirisc_common {
+       struct target *target;
+       struct esirisc_jtag jtag_info;
+       enum esirisc_cache cache_arch;
+       char *gdb_arch;
+
+       struct reg_cache *reg_cache;
+       struct reg *epc;
+       struct reg *ecas;
+       struct reg *eid;
+       struct reg *ed;
+       uint32_t etc_save;
+       uint32_t hwdc_save;
+
+       int num_bits;
+       int num_regs;
+       bool has_icache;
+       bool has_dcache;
+       int num_breakpoints;
+       int num_watchpoints;
+
+       struct breakpoint *breakpoints_p[MAX_BREAKPOINTS];
+       struct watchpoint *watchpoints_p[MAX_WATCHPOINTS];
+};
+
+union esirisc_memory {
+       uint32_t word;
+       uint16_t hword;
+       uint8_t byte;
+};
+
+struct esirisc_reg {
+       struct esirisc_common *esirisc;
+
+       uint8_t bank;
+       uint8_t csr;
+
+       int (*read)(struct reg *reg);
+       int (*write)(struct reg *reg);
+};
+
+static inline struct esirisc_common *target_to_esirisc(struct target *target)
+{
+       return (struct esirisc_common *)target->arch_info;
+}
+
+static inline char *esirisc_cache_arch(struct esirisc_common *esirisc)
+{
+       return esirisc->cache_arch == ESIRISC_CACHE_HARVARD ? "harvard" : "von_neumann";
+}
+
+static inline bool esirisc_has_cache(struct esirisc_common *esirisc)
+{
+       return esirisc->has_icache || esirisc->has_dcache;
+}
+
+#endif /* OPENOCD_TARGET_ESIRISC_H */
diff --git a/src/target/esirisc_jtag.c b/src/target/esirisc_jtag.c
new file mode 100644 (file)
index 0000000..8ab47fa
--- /dev/null
@@ -0,0 +1,514 @@
+/***************************************************************************
+ *   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/log.h>
+#include <helper/types.h>
+#include <jtag/jtag.h>
+#include <jtag/commands.h>
+#include <jtag/interface.h>
+
+#include "esirisc_jtag.h"
+
+static void esirisc_jtag_set_instr(struct esirisc_jtag *jtag_info, uint32_t new_instr)
+{
+       struct jtag_tap *tap = jtag_info->tap;
+
+       if (buf_get_u32(tap->cur_instr, 0, tap->ir_length) != new_instr) {
+               struct scan_field field;
+               uint8_t t[4];
+
+               field.num_bits = tap->ir_length;
+               field.out_value = t;
+               buf_set_u32(t, 0, field.num_bits, new_instr);
+               field.in_value = NULL;
+
+               jtag_add_ir_scan(tap, &field, TAP_IDLE);
+       }
+}
+
+/*
+ * The data register is latched every 8 bits while in the Shift-DR state
+ * (Update-DR is not supported). This necessitates prepending padding
+ * bits to ensure data is aligned when multiple TAPs are present.
+ */
+static int esirisc_jtag_get_padding(void)
+{
+       int padding = 0;
+       int bypass_devices = 0;
+
+       for (struct jtag_tap *tap = jtag_tap_next_enabled(NULL); tap != NULL;
+                       tap = jtag_tap_next_enabled(tap))
+               if (tap->bypass)
+                       bypass_devices++;
+
+       int num_bits = bypass_devices % 8;
+       if (num_bits > 0)
+               padding = 8 - num_bits;
+
+       return padding;
+}
+
+static int esirisc_jtag_count_bits(int num_fields, struct scan_field *fields)
+{
+       int bit_count = 0;
+
+       for (int i = 0; i < num_fields; ++i)
+               bit_count += fields[i].num_bits;
+
+       return bit_count;
+}
+
+/*
+ * Data received from the target will be byte-stuffed if it contains
+ * either the pad byte (0xAA) or stuffing marker (0x55). Buffers should
+ * be sized twice the expected length to account for stuffing overhead.
+ */
+static void esirisc_jtag_unstuff(uint8_t *data, size_t len)
+{
+       uint8_t *r, *w;
+       uint8_t *end;
+
+       r = w = data;
+       end = data + len;
+       while (r < end) {
+               if (*r == STUFF_MARKER) {
+                       r++; /* skip stuffing marker */
+                       assert(r < end);
+                       *w++ = *r++ ^ STUFF_MARKER;
+               } else
+                       *w++ = *r++;
+       }
+}
+
+/*
+ * The eSi-Debug protocol defines a byte-oriented command/response
+ * channel that operates over serial or JTAG. While not strictly
+ * required, separate DR scans are used for sending and receiving data.
+ * This allows the TAP to recover gracefully if the byte stream is
+ * corrupted at the expense of sending additional padding bits.
+ */
+
+static int esirisc_jtag_send(struct esirisc_jtag *jtag_info, uint8_t command,
+               int num_out_fields, struct scan_field *out_fields)
+{
+       int num_fields = 2 + num_out_fields;
+       struct scan_field *fields = cmd_queue_alloc(num_fields * sizeof(struct scan_field));
+
+       esirisc_jtag_set_instr(jtag_info, INSTR_DEBUG);
+
+       fields[0].num_bits = esirisc_jtag_get_padding();
+       fields[0].out_value = NULL;
+       fields[0].in_value = NULL;
+
+       fields[1].num_bits = 8;
+       fields[1].out_value = &command;
+       fields[1].in_value = NULL;
+
+       /* append command data */
+       for (int i = 0; i < num_out_fields; ++i)
+               jtag_scan_field_clone(&fields[2+i], &out_fields[i]);
+
+       jtag_add_dr_scan(jtag_info->tap, num_fields, fields, TAP_IDLE);
+
+       return jtag_execute_queue();
+}
+
+static int esirisc_jtag_recv(struct esirisc_jtag *jtag_info,
+               int num_in_fields, struct scan_field *in_fields)
+{
+       int num_in_bits = esirisc_jtag_count_bits(num_in_fields, in_fields);
+       int num_in_bytes = DIV_ROUND_UP(num_in_bits, 8);
+
+       struct scan_field fields[3];
+       uint8_t r[num_in_bytes * 2];
+
+       esirisc_jtag_set_instr(jtag_info, INSTR_DEBUG);
+
+       fields[0].num_bits = esirisc_jtag_get_padding() + 1;
+       fields[0].out_value = NULL;
+       fields[0].in_value = NULL;
+
+       fields[1].num_bits = 8;
+       fields[1].out_value = NULL;
+       fields[1].in_value = &jtag_info->status;
+
+       fields[2].num_bits = num_in_bits * 2;
+       fields[2].out_value = NULL;
+       fields[2].in_value = r;
+
+       jtag_add_dr_scan(jtag_info->tap, ARRAY_SIZE(fields), fields, TAP_IDLE);
+
+       int retval = jtag_execute_queue();
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* unstuff response data and write back to caller */
+       if (num_in_fields > 0) {
+               esirisc_jtag_unstuff(r, ARRAY_SIZE(r));
+
+               int bit_count = 0;
+               for (int i = 0; i < num_in_fields; ++i) {
+                       buf_set_buf(r, bit_count, in_fields[i].in_value, 0, in_fields[i].num_bits);
+                       bit_count += in_fields[i].num_bits;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_jtag_check_status(struct esirisc_jtag *jtag_info)
+{
+       uint8_t eid = esirisc_jtag_get_eid(jtag_info);
+       if (eid != EID_NONE) {
+               LOG_ERROR("esirisc_jtag: bad status: 0x%02" PRIx32 " (DA: %" PRId32 ", "
+                               "S: %" PRId32 ", EID: 0x%02" PRIx32 ")",
+                               jtag_info->status, esirisc_jtag_is_debug_active(jtag_info),
+                               esirisc_jtag_is_stopped(jtag_info), eid);
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+static int esirisc_jtag_send_and_recv(struct esirisc_jtag *jtag_info, uint8_t command,
+               int num_out_fields, struct scan_field *out_fields,
+               int num_in_fields, struct scan_field *in_fields)
+{
+       int retval;
+
+       jtag_info->status = 0;  /* clear status */
+
+       retval = esirisc_jtag_send(jtag_info, command, num_out_fields, out_fields);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("esirisc_jtag: send failed (command: 0x%02" PRIx32 ")", command);
+               return ERROR_FAIL;
+       }
+
+       retval = esirisc_jtag_recv(jtag_info, num_in_fields, in_fields);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("esirisc_jtag: recv failed (command: 0x%02" PRIx32 ")", command);
+               return ERROR_FAIL;
+       }
+
+       return esirisc_jtag_check_status(jtag_info);
+}
+
+/*
+ * Status is automatically updated after each command completes;
+ * these functions make each field available to the caller.
+ */
+
+bool esirisc_jtag_is_debug_active(struct esirisc_jtag *jtag_info)
+{
+       return !!(jtag_info->status & 1<<7);    /* DA */
+}
+
+bool esirisc_jtag_is_stopped(struct esirisc_jtag *jtag_info)
+{
+       return !!(jtag_info->status & 1<<6);    /* S */
+}
+
+uint8_t esirisc_jtag_get_eid(struct esirisc_jtag *jtag_info)
+{
+       return jtag_info->status & 0x3f;                /* EID */
+}
+
+/*
+ * Most commands manipulate target data (eg. memory and registers); each
+ * command returns a status byte that indicates success. Commands must
+ * transmit multibyte values in big-endian order, however response
+ * values are in little-endian order. Target endianness does not have an
+ * effect on this ordering.
+ */
+
+int esirisc_jtag_read_byte(struct esirisc_jtag *jtag_info, uint32_t address, uint8_t *data)
+{
+       struct scan_field out_fields[1];
+       uint8_t a[4];
+
+       out_fields[0].num_bits = 32;
+       out_fields[0].out_value = a;
+       h_u32_to_be(a, address);
+       out_fields[0].in_value = NULL;
+
+       struct scan_field in_fields[1];
+       uint8_t d[1];
+
+       in_fields[0].num_bits = 8;
+       in_fields[0].out_value = NULL;
+       in_fields[0].in_value = d;
+
+       int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_BYTE,
+                       ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields);
+       if (retval != ERROR_OK)
+               return retval;
+
+       *data = *d;
+
+       return ERROR_OK;
+}
+
+int esirisc_jtag_read_hword(struct esirisc_jtag *jtag_info, uint32_t address, uint16_t *data)
+{
+       struct scan_field out_fields[1];
+       uint8_t a[4];
+
+       out_fields[0].num_bits = 32;
+       out_fields[0].out_value = a;
+       h_u32_to_be(a, address);
+       out_fields[0].in_value = NULL;
+
+       struct scan_field in_fields[1];
+       uint8_t d[2];
+
+       in_fields[0].num_bits = 16;
+       in_fields[0].out_value = NULL;
+       in_fields[0].in_value = d;
+
+       int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_HWORD,
+                       ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields);
+       if (retval != ERROR_OK)
+               return retval;
+
+       *data = le_to_h_u16(d);
+
+       return ERROR_OK;
+}
+
+int esirisc_jtag_read_word(struct esirisc_jtag *jtag_info, uint32_t address, uint32_t *data)
+{
+       struct scan_field out_fields[1];
+       uint8_t a[4];
+
+       out_fields[0].num_bits = 32;
+       out_fields[0].out_value = a;
+       h_u32_to_be(a, address);
+       out_fields[0].in_value = NULL;
+
+       struct scan_field in_fields[1];
+       uint8_t d[4];
+
+       in_fields[0].num_bits = 32;
+       in_fields[0].out_value = NULL;
+       in_fields[0].in_value = d;
+
+       int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_WORD,
+                       ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields);
+       if (retval != ERROR_OK)
+               return retval;
+
+       *data = le_to_h_u32(d);
+
+       return ERROR_OK;
+}
+
+int esirisc_jtag_write_byte(struct esirisc_jtag *jtag_info, uint32_t address, uint8_t data)
+{
+       struct scan_field out_fields[2];
+       uint8_t a[4];
+
+       out_fields[0].num_bits = 32;
+       out_fields[0].out_value = a;
+       h_u32_to_be(a, address);
+       out_fields[0].in_value = NULL;
+
+       out_fields[1].num_bits = 8;
+       out_fields[1].out_value = &data;
+       out_fields[1].in_value = NULL;
+
+       return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_BYTE,
+                       ARRAY_SIZE(out_fields), out_fields, 0, NULL);
+}
+
+int esirisc_jtag_write_hword(struct esirisc_jtag *jtag_info, uint32_t address, uint16_t data)
+{
+       struct scan_field out_fields[2];
+       uint8_t a[4], d[2];
+
+       out_fields[0].num_bits = 32;
+       out_fields[0].out_value = a;
+       h_u32_to_be(a, address);
+       out_fields[0].in_value = NULL;
+
+       out_fields[1].num_bits = 16;
+       out_fields[1].out_value = d;
+       h_u16_to_be(d, data);
+       out_fields[1].in_value = NULL;
+
+       return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_HWORD,
+                       ARRAY_SIZE(out_fields), out_fields, 0, NULL);
+}
+
+int esirisc_jtag_write_word(struct esirisc_jtag *jtag_info, uint32_t address, uint32_t data)
+{
+       struct scan_field out_fields[2];
+       uint8_t a[4], d[4];
+
+       out_fields[0].num_bits = 32;
+       out_fields[0].out_value = a;
+       h_u32_to_be(a, address);
+       out_fields[0].in_value = NULL;
+
+       out_fields[1].num_bits = 32;
+       out_fields[1].out_value = d;
+       h_u32_to_be(d, data);
+       out_fields[1].in_value = NULL;
+
+       return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_WORD,
+                       ARRAY_SIZE(out_fields), out_fields, 0, NULL);
+}
+
+int esirisc_jtag_read_reg(struct esirisc_jtag *jtag_info, uint8_t reg, uint32_t *data)
+{
+       struct scan_field out_fields[1];
+
+       out_fields[0].num_bits = 8;
+       out_fields[0].out_value = &reg;
+       out_fields[0].in_value = NULL;
+
+       struct scan_field in_fields[1];
+       uint8_t d[4];
+
+       in_fields[0].num_bits = 32;
+       in_fields[0].out_value = NULL;
+       in_fields[0].in_value = d;
+
+       int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_REG,
+                       ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields);
+       if (retval != ERROR_OK)
+               return retval;
+
+       *data = le_to_h_u32(d);
+
+       return ERROR_OK;
+}
+
+int esirisc_jtag_write_reg(struct esirisc_jtag *jtag_info, uint8_t reg, uint32_t data)
+{
+       struct scan_field out_fields[2];
+       uint8_t d[4];
+
+       out_fields[0].num_bits = 8;
+       out_fields[0].out_value = &reg;
+       out_fields[0].in_value = NULL;
+
+       out_fields[1].num_bits = 32;
+       out_fields[1].out_value = d;
+       h_u32_to_be(d, data);
+       out_fields[1].in_value = NULL;
+
+       return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_REG,
+                       ARRAY_SIZE(out_fields), out_fields, 0, NULL);
+}
+
+int esirisc_jtag_read_csr(struct esirisc_jtag *jtag_info, uint8_t bank, uint8_t csr, uint32_t *data)
+{
+       struct scan_field out_fields[1];
+       uint8_t c[2];
+
+       out_fields[0].num_bits = 16;
+       out_fields[0].out_value = c;
+       h_u16_to_be(c, (csr << 5) | bank);
+       out_fields[0].in_value = NULL;
+
+       struct scan_field in_fields[1];
+       uint8_t d[4];
+
+       in_fields[0].num_bits = 32;
+       in_fields[0].out_value = NULL;
+       in_fields[0].in_value = d;
+
+       int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_CSR,
+                       ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields);
+       if (retval != ERROR_OK)
+               return retval;
+
+       *data = le_to_h_u32(d);
+
+       return ERROR_OK;
+}
+
+int esirisc_jtag_write_csr(struct esirisc_jtag *jtag_info, uint8_t bank, uint8_t csr, uint32_t data)
+{
+       struct scan_field out_fields[2];
+       uint8_t c[2], d[4];
+
+       out_fields[0].num_bits = 16;
+       out_fields[0].out_value = c;
+       h_u16_to_be(c, (csr << 5) | bank);
+       out_fields[0].in_value = NULL;
+
+       out_fields[1].num_bits = 32;
+       out_fields[1].out_value = d;
+       h_u32_to_be(d, data);
+       out_fields[1].in_value = NULL;
+
+       return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_CSR,
+                       ARRAY_SIZE(out_fields), out_fields, 0, NULL);
+}
+
+/*
+ * Control commands affect CPU operation; these commands send no data
+ * and return a status byte.
+ */
+
+static inline int esirisc_jtag_send_ctrl(struct esirisc_jtag *jtag_info, uint8_t command)
+{
+       return esirisc_jtag_send_and_recv(jtag_info, command, 0, NULL, 0, NULL);
+}
+
+int esirisc_jtag_enable_debug(struct esirisc_jtag *jtag_info)
+{
+       return esirisc_jtag_send_ctrl(jtag_info, DEBUG_ENABLE_DEBUG);
+}
+
+int esirisc_jtag_disable_debug(struct esirisc_jtag *jtag_info)
+{
+       return esirisc_jtag_send_ctrl(jtag_info, DEBUG_DISABLE_DEBUG);
+}
+
+int esirisc_jtag_assert_reset(struct esirisc_jtag *jtag_info)
+{
+       return esirisc_jtag_send_ctrl(jtag_info, DEBUG_ASSERT_RESET);
+}
+
+int esirisc_jtag_deassert_reset(struct esirisc_jtag *jtag_info)
+{
+       return esirisc_jtag_send_ctrl(jtag_info, DEBUG_DEASSERT_RESET);
+}
+
+int esirisc_jtag_break(struct esirisc_jtag *jtag_info)
+{
+       return esirisc_jtag_send_ctrl(jtag_info, DEBUG_BREAK);
+}
+
+int esirisc_jtag_continue(struct esirisc_jtag *jtag_info)
+{
+       return esirisc_jtag_send_ctrl(jtag_info, DEBUG_CONTINUE);
+}
+
+int esirisc_jtag_flush_caches(struct esirisc_jtag *jtag_info)
+{
+       return esirisc_jtag_send_ctrl(jtag_info, DEBUG_FLUSH_CACHES);
+}
diff --git a/src/target/esirisc_jtag.h b/src/target/esirisc_jtag.h
new file mode 100644 (file)
index 0000000..8189ddc
--- /dev/null
@@ -0,0 +1,104 @@
+/***************************************************************************
+ *   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/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ESIRISC_JTAG_H
+#define OPENOCD_TARGET_ESIRISC_JTAG_H
+
+#include <jtag/jtag.h>
+
+/* TAP Instructions */
+#define INSTR_IDCODE                   0x8
+#define INSTR_DEBUG                            0x9
+#define INSTR_BYPASS                   0xf
+#define INSTR_LENGTH                   4
+
+/* eSi-Debug Commands */
+#define DEBUG_NOP                              0x00
+#define DEBUG_READ_BYTE                        0x10
+#define DEBUG_READ_HWORD               0x20
+#define DEBUG_READ_WORD                        0x30
+#define DEBUG_WRITE_BYTE               0x60
+#define DEBUG_WRITE_HWORD              0x70
+#define DEBUG_WRITE_WORD               0x80
+#define DEBUG_READ_REG                 0xb0
+#define DEBUG_WRITE_REG                        0xc0
+#define DEBUG_READ_CSR                 0xd0
+#define DEBUG_WRITE_CSR                        0xe0
+#define DEBUG_ENABLE_DEBUG             0xf0
+#define DEBUG_DISABLE_DEBUG            0xf2
+#define DEBUG_ASSERT_RESET             0xf4
+#define DEBUG_DEASSERT_RESET   0xf6
+#define DEBUG_BREAK                            0xf8
+#define DEBUG_CONTINUE                 0xfa
+#define DEBUG_FLUSH_CACHES             0xfc
+
+/* Exception IDs */
+#define EID_OVERFLOW                   0x3d
+#define EID_CANT_DEBUG                 0x3e
+#define EID_NONE                               0x3f
+
+/* Byte Stuffing */
+#define STUFF_MARKER                   0x55
+#define PAD_BYTE                               0xaa
+
+struct esirisc_jtag {
+       struct jtag_tap *tap;
+       uint8_t status;
+};
+
+bool esirisc_jtag_is_debug_active(struct esirisc_jtag *jtag_info);
+bool esirisc_jtag_is_stopped(struct esirisc_jtag *jtag_info);
+uint8_t esirisc_jtag_get_eid(struct esirisc_jtag *jtag_info);
+
+int esirisc_jtag_read_byte(struct esirisc_jtag *jtag_info,
+               uint32_t address, uint8_t *data);
+int esirisc_jtag_read_hword(struct esirisc_jtag *jtag_info,
+               uint32_t address, uint16_t *data);
+int esirisc_jtag_read_word(struct esirisc_jtag *jtag_info,
+               uint32_t address, uint32_t *data);
+
+int esirisc_jtag_write_byte(struct esirisc_jtag *jtag_info,
+               uint32_t address, uint8_t data);
+int esirisc_jtag_write_hword(struct esirisc_jtag *jtag_info,
+               uint32_t address, uint16_t data);
+int esirisc_jtag_write_word(struct esirisc_jtag *jtag_info,
+               uint32_t address, uint32_t data);
+
+int esirisc_jtag_read_reg(struct esirisc_jtag *jtag_info,
+               uint8_t reg, uint32_t *data);
+int esirisc_jtag_write_reg(struct esirisc_jtag *jtag_info,
+               uint8_t reg, uint32_t data);
+
+int esirisc_jtag_read_csr(struct esirisc_jtag *jtag_info,
+               uint8_t bank, uint8_t csr, uint32_t *data);
+int esirisc_jtag_write_csr(struct esirisc_jtag *jtag_info,
+               uint8_t bank, uint8_t csr, uint32_t data);
+
+int esirisc_jtag_enable_debug(struct esirisc_jtag *jtag_info);
+int esirisc_jtag_disable_debug(struct esirisc_jtag *jtag_info);
+
+int esirisc_jtag_assert_reset(struct esirisc_jtag *jtag_info);
+int esirisc_jtag_deassert_reset(struct esirisc_jtag *jtag_info);
+
+int esirisc_jtag_break(struct esirisc_jtag *jtag_info);
+int esirisc_jtag_continue(struct esirisc_jtag *jtag_info);
+
+int esirisc_jtag_flush_caches(struct esirisc_jtag *jtag_info);
+
+#endif /* OPENOCD_TARGET_ESIRISC_JTAG_H */
diff --git a/src/target/esirisc_regs.h b/src/target/esirisc_regs.h
new file mode 100644 (file)
index 0000000..ad33858
--- /dev/null
@@ -0,0 +1,184 @@
+/***************************************************************************
+ *   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/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ESIRISC_REGS_H
+#define OPENOCD_TARGET_ESIRISC_REGS_H
+
+enum esirisc_reg_num {
+       ESIRISC_SP,
+       ESIRISC_RA,
+       ESIRISC_R2,
+       ESIRISC_R3,
+       ESIRISC_R4,
+       ESIRISC_R5,
+       ESIRISC_R6,
+       ESIRISC_R7,
+       ESIRISC_R8,
+       ESIRISC_R9,
+       ESIRISC_R10,
+       ESIRISC_R11,
+       ESIRISC_R12,
+       ESIRISC_R13,
+       ESIRISC_R14,
+       ESIRISC_R15,
+       ESIRISC_R16,
+       ESIRISC_R17,
+       ESIRISC_R18,
+       ESIRISC_R19,
+       ESIRISC_R20,
+       ESIRISC_R21,
+       ESIRISC_R22,
+       ESIRISC_R23,
+       ESIRISC_R24,
+       ESIRISC_R25,
+       ESIRISC_R26,
+       ESIRISC_R27,
+       ESIRISC_R28,
+       ESIRISC_R29,
+       ESIRISC_R30,
+       ESIRISC_R31,
+
+       ESIRISC_V0,
+       ESIRISC_V1,
+       ESIRISC_V2,
+       ESIRISC_V3,
+       ESIRISC_V4,
+       ESIRISC_V5,
+       ESIRISC_V6,
+       ESIRISC_V7,
+       ESIRISC_V8,
+       ESIRISC_V9,
+       ESIRISC_V10,
+       ESIRISC_V11,
+       ESIRISC_V12,
+       ESIRISC_V13,
+       ESIRISC_V14,
+       ESIRISC_V15,
+       ESIRISC_V16,
+       ESIRISC_V17,
+       ESIRISC_V18,
+       ESIRISC_V19,
+       ESIRISC_V20,
+       ESIRISC_V21,
+       ESIRISC_V22,
+       ESIRISC_V23,
+       ESIRISC_V24,
+       ESIRISC_V25,
+       ESIRISC_V26,
+       ESIRISC_V27,
+       ESIRISC_V28,
+       ESIRISC_V29,
+       ESIRISC_V30,
+       ESIRISC_V31,
+
+       ESIRISC_A0,
+       ESIRISC_A1,
+       ESIRISC_A2,
+       ESIRISC_A3,
+       ESIRISC_A4,
+       ESIRISC_A5,
+       ESIRISC_A6,
+       ESIRISC_A7,
+
+       ESIRISC_PC,
+       ESIRISC_CAS,
+       ESIRISC_TC,
+       ESIRISC_ETA,
+       ESIRISC_ETC,
+       ESIRISC_EPC,
+       ESIRISC_ECAS,
+       ESIRISC_EID,
+       ESIRISC_ED,
+       ESIRISC_IP,
+       ESIRISC_IM,
+       ESIRISC_IS,
+       ESIRISC_IT,
+
+       ESIRISC_NUM_REGS,
+};
+
+/* CSR Banks */
+#define CSR_THREAD                                     0x00
+#define CSR_INTERRUPT                          0x01
+#define CSR_DEBUG                                      0x04
+#define CSR_CONFIG                                     0x05
+#define CSR_TRACE                                      0x09
+
+/* Thread CSRs */
+#define CSR_THREAD_TC                          0x00    /* Thread Control */
+#define CSR_THREAD_PC                          0x01    /* Program Counter */
+#define CSR_THREAD_CAS                         0x02    /* Comparison & Arithmetic Status */
+#define CSR_THREAD_AC                          0x03    /* Arithmetic Control */
+#define CSR_THREAD_LF                          0x04    /* Locked Flag */
+#define CSR_THREAD_LA                          0x05    /* Locked Address */
+#define CSR_THREAD_ETA                         0x07    /* Exception Table Address */
+#define CSR_THREAD_ETC                         0x08    /* Exception TC */
+#define CSR_THREAD_EPC                         0x09    /* Exception PC */
+#define CSR_THREAD_ECAS                                0x0a    /* Exception CAS */
+#define CSR_THREAD_EID                         0x0b    /* Exception ID */
+#define CSR_THREAD_ED                          0x0c    /* Exception Data */
+
+/* Interrupt CSRs */
+#define CSR_INTERRUPT_IP                       0x00    /* Interrupt Pending */
+#define CSR_INTERRUPT_IA                       0x01    /* Interrupt Acknowledge */
+#define CSR_INTERRUPT_IM                       0x02    /* Interrupt Mask */
+#define CSR_INTERRUPT_IS                       0x03    /* Interrupt Sense */
+#define CSR_INTERRUPT_IT                       0x04    /* Interrupt Trigger */
+
+/* Debug CSRs */
+#define CSR_DEBUG_DC                           0x00    /* Debug Control */
+#define CSR_DEBUG_IBC                          0x01    /* Instruction Breakpoint Control */
+#define CSR_DEBUG_DBC                          0x02    /* Data Breakpoint Control */
+#define CSR_DEBUG_HWDC                         0x03    /* Hardware Debug Control */
+#define CSR_DEBUG_DBS                          0x04    /* Data Breakpoint Size */
+#define CSR_DEBUG_DBR                          0x05    /* Data Breakpoint Range */
+#define CSR_DEBUG_IBAn                         0x08    /* Instruction Breakpoint Address [0..7] */
+#define CSR_DEBUG_DBAn                         0x10    /* Data Breakpoint Address [0..7] */
+
+/* Configuration CSRs */
+#define CSR_CONFIG_ARCH0                       0x00    /* Architectural Configuration 0 */
+#define CSR_CONFIG_ARCH1                       0x01    /* Architectural Configuration 1 */
+#define CSR_CONFIG_ARCH2                       0x02    /* Architectural Configuration 2 */
+#define CSR_CONFIG_ARCH3                       0x03    /* Architectural Configuration 3 */
+#define CSR_CONFIG_MEM                         0x04    /* Memory Configuration */
+#define CSR_CONFIG_IC                          0x05    /* Instruction Cache Configuration */
+#define CSR_CONFIG_DC                          0x06    /* Data Cache Configuration */
+#define CSR_CONFIG_INT                         0x07    /* Interrupt Configuration */
+#define        CSR_CONFIG_ISAn                         0x08    /* Instruction Set Configuration [0..6] */
+#define CSR_CONFIG_DBG                         0x0f    /* Debug Configuration */
+#define CSR_CONFIG_MID                         0x10    /* Manufacturer ID */
+#define CSR_CONFIG_REV                         0x11    /* Revision Number */
+#define CSR_CONFIG_MPID                                0x12    /* Mulitprocessor ID */
+#define CSR_CONFIG_FREQn                       0x13    /* Frequency [0..2] */
+#define CSR_CONFIG_TRACE                       0x16    /* Trace Configuration */
+
+/* Trace CSRs */
+#define CSR_TRACE_CONTROL                      0x00
+#define CSR_TRACE_STATUS                       0x01
+#define CSR_TRACE_BUFFER_START         0x02
+#define CSR_TRACE_BUFFER_END           0x03
+#define CSR_TRACE_BUFFER_CUR           0x04
+#define CSR_TRACE_TRIGGER                      0x05
+#define CSR_TRACE_START_DATA           0x06
+#define CSR_TRACE_START_MASK           0x07
+#define CSR_TRACE_STOP_DATA                    0x08
+#define CSR_TRACE_STOP_MASK                    0x09
+#define CSR_TRACE_DELAY                                0x0a
+
+#endif /* OPENOCD_TARGET_ESIRISC_REGS_H */
index c1ccf962e9cd514f12394d6f2e089d60e9738d9b..bf3669192ea7dc92f65ed2848d89fcfde0b19b62 100644 (file)
@@ -109,6 +109,7 @@ extern struct target_type quark_d20xx_target;
 extern struct target_type stm8_target;
 extern struct target_type riscv_target;
 extern struct target_type mem_ap_target;
+extern struct target_type esirisc_target;
 
 static struct target_type *target_types[] = {
        &arm7tdmi_target,
@@ -142,10 +143,11 @@ static struct target_type *target_types[] = {
        &quark_d20xx_target,
        &stm8_target,
        &riscv_target,
+       &mem_ap_target,
+       &esirisc_target,
 #if BUILD_TARGET64
        &aarch64_target,
 #endif
-       &mem_ap_target,
        NULL,
 };
 
index 5457f0abf63f2240f3b2a294b6312a10d2e899ed..d7961313887bcd5795b488c7e6a03d52ffbaa87c 100644 (file)
@@ -225,6 +225,13 @@ struct gdb_fileio_info {
        uint64_t param_4;
 };
 
+/** Returns a description of the endianness for the specified target. */
+static inline const char *target_endianness(struct target *target)
+{
+       return (target->endianness == TARGET_ENDIAN_UNKNOWN) ? "unknown" :
+                       (target->endianness == TARGET_BIG_ENDIAN) ? "big endian" : "little endian";
+}
+
 /** Returns the instance-specific name of the specified target. */
 static inline const char *target_name(struct target *target)
 {
diff --git a/tcl/target/esi32xx.cfg b/tcl/target/esi32xx.cfg
new file mode 100644 (file)
index 0000000..d32af39
--- /dev/null
@@ -0,0 +1,36 @@
+#
+# EnSilica eSi-32xx SoC (eSi-RISC Family)
+# http://www.ensilica.com/risc-ip/
+#
+
+if { [info exists CHIPNAME] } {
+    set _CHIPNAME $CHIPNAME
+} else {
+    set _CHIPNAME esi32xx
+}
+
+if { [info exists CPUTAPID] } {
+    set _CPUTAPID $CPUTAPID
+} else {
+    set _CPUTAPID 0x11234001
+}
+
+jtag newtap $_CHIPNAME cpu -irlen 4 -expected-id $_CPUTAPID
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME esirisc -chain-position $_CHIPNAME.cpu
+
+# Targets with the UNIFIED_ADDRESS_SPACE option disabled should set
+# CACHEARCH to 'harvard'. By default, 'von_neumann' is assumed.
+if { [info exists CACHEARCH] } {
+    $_TARGETNAME esirisc cache_arch $CACHEARCH
+}
+
+adapter_khz 2000
+
+reset_config none
+
+# 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.
+gdb_breakpoint_override hard

Linking to existing account procedure

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

SSH host keys fingerprints

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