aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/target/Makefile.am13
-rw-r--r--src/target/arc.c1339
-rw-r--r--src/target/arc.h212
-rw-r--r--src/target/arc_cmd.c977
-rw-r--r--src/target/arc_cmd.h16
-rw-r--r--src/target/arc_jtag.c542
-rw-r--r--src/target/arc_jtag.h70
-rw-r--r--src/target/arc_mem.c287
-rw-r--r--src/target/arc_mem.h21
-rw-r--r--src/target/target.c2
10 files changed, 3478 insertions, 1 deletions
diff --git a/src/target/Makefile.am b/src/target/Makefile.am
index 5a16def..30d2339 100644
--- a/src/target/Makefile.am
+++ b/src/target/Makefile.am
@@ -24,6 +24,7 @@ noinst_LTLIBRARIES += %D%/libtarget.la
$(STM8_SRC) \
$(INTEL_IA32_SRC) \
$(ESIRISC_SRC) \
+ $(ARC_SRC) \
%D%/avrt.c \
%D%/dsp563xx.c \
%D%/dsp563xx_once.c \
@@ -156,6 +157,12 @@ ESIRISC_SRC = \
%D%/esirisc_jtag.c \
%D%/esirisc_trace.c
+ARC_SRC = \
+ %D%/arc.c \
+ %D%/arc_cmd.c \
+ %D%/arc_jtag.c \
+ %D%/arc_mem.c
+
%C%_libtarget_la_SOURCES += \
%D%/algorithm.h \
%D%/arm.h \
@@ -243,7 +250,11 @@ ESIRISC_SRC = \
%D%/esirisc.h \
%D%/esirisc_jtag.h \
%D%/esirisc_regs.h \
- %D%/esirisc_trace.h
+ %D%/esirisc_trace.h \
+ %D%/arc.h \
+ %D%/arc_cmd.h \
+ %D%/arc_jtag.h \
+ %D%/arc_mem.h
include %D%/openrisc/Makefile.am
include %D%/riscv/Makefile.am
diff --git a/src/target/arc.c b/src/target/arc.c
new file mode 100644
index 0000000..45ef725
--- /dev/null
+++ b/src/target/arc.c
@@ -0,0 +1,1339 @@
+/***************************************************************************
+ * Copyright (C) 2013-2015,2019-2020 Synopsys, Inc. *
+ * Frank Dols <frank.dols@synopsys.com> *
+ * Mischa Jonker <mischa.jonker@synopsys.com> *
+ * Anton Kolesov <anton.kolesov@synopsys.com> *
+ * Evgeniy Didin <didin@synopsys.com> *
+ * *
+ * SPDX-License-Identifier: GPL-2.0-or-later *
+ ***************************************************************************/
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "arc.h"
+
+
+
+/*
+ * ARC architecture specific details.
+ *
+ * ARC has two types of registers:
+ * 1) core registers(e.g. r0,r1..) [is_core = true]
+ * 2) Auxiliary registers [is_core = false]..
+ *
+ * Auxiliary registers at the same time can be divided into
+ * read-only BCR(build configuration regs, e.g. isa_config, mpu_build) and
+ * R/RW non-BCR ("control" register, e.g. pc, status32_t, debug).
+ *
+ * The way of accessing to Core and AUX registers differs on Jtag level.
+ * BCR/non-BCR describes if the register is immutable and that reading
+ * unexisting register is safe RAZ, rather then an error.
+ * Note, core registers cannot be BCR.
+ *
+ * In arc/cpu/ tcl files all regiters are defined as core, non-BCR aux
+ * and BCR aux, in "add-reg" command they are passed to three lists
+ * respectively: core_reg_descriptions, aux_reg_descriptions,
+ * bcr_reg_descriptions.
+ *
+ * Due to the specifics of accessing to BCR/non-BCR registers there are two
+ * register caches:
+ * 1) core_and_aux_cache - includes registers described in
+ * core_reg_descriptions and aux_reg_descriptions lists.
+ * Used during save/restore context step.
+ * 2) bcr_cache - includes registers described bcr_reg_descriptions.
+ * Currently used internally during configure step.
+ */
+
+
+
+void arc_reg_data_type_add(struct target *target,
+ struct arc_reg_data_type *data_type)
+{
+ LOG_DEBUG("Adding %s reg_data_type", data_type->data_type.id);
+ struct arc_common *arc = target_to_arc(target);
+ assert(arc);
+
+ list_add_tail(&data_type->list, &arc->reg_data_types);
+}
+
+/**
+ * Private implementation of register_get_by_name() for ARC that
+ * doesn't skip not [yet] existing registers. Used in many places
+ * for iteration through registers and even for marking required registers as
+ * existing.
+ */
+struct reg *arc_reg_get_by_name(struct reg_cache *first,
+ const char *name, bool search_all)
+{
+ unsigned int i;
+ struct reg_cache *cache = first;
+
+ while (cache) {
+ for (i = 0; i < cache->num_regs; i++) {
+ if (!strcmp(cache->reg_list[i].name, name))
+ return &(cache->reg_list[i]);
+ }
+
+ if (search_all)
+ cache = cache->next;
+ else
+ break;
+ }
+
+ return NULL;
+}
+
+
+/* Initialize arc_common structure, which passes to openocd target instance */
+static int arc_init_arch_info(struct target *target, struct arc_common *arc,
+ struct jtag_tap *tap)
+{
+ arc->common_magic = ARC_COMMON_MAGIC;
+ target->arch_info = arc;
+
+ arc->jtag_info.tap = tap;
+
+ /* The only allowed ir_length is 4 for ARC jtag. */
+ if (tap->ir_length != 4) {
+ LOG_ERROR("ARC jtag instruction length should be equal to 4");
+ return ERROR_FAIL;
+ }
+
+ /* Add standard GDB data types */
+ INIT_LIST_HEAD(&arc->reg_data_types);
+ struct arc_reg_data_type *std_types = calloc(ARRAY_SIZE(standard_gdb_types),
+ sizeof(*std_types));
+
+ if (!std_types) {
+ LOG_ERROR("Unable to allocate memory");
+ return ERROR_FAIL;
+ }
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(standard_gdb_types); i++) {
+ std_types[i].data_type.type = standard_gdb_types[i].type;
+ std_types[i].data_type.id = standard_gdb_types[i].id;
+ arc_reg_data_type_add(target, &(std_types[i]));
+ }
+
+ /* Fields related to target descriptions */
+ INIT_LIST_HEAD(&arc->core_reg_descriptions);
+ INIT_LIST_HEAD(&arc->aux_reg_descriptions);
+ INIT_LIST_HEAD(&arc->bcr_reg_descriptions);
+ arc->num_regs = 0;
+ arc->num_core_regs = 0;
+ arc->num_aux_regs = 0;
+ arc->num_bcr_regs = 0;
+ arc->last_general_reg = ULONG_MAX;
+ arc->pc_index_in_cache = ULONG_MAX;
+ arc->debug_index_in_cache = ULONG_MAX;
+
+ return ERROR_OK;
+}
+
+int arc_reg_add(struct target *target, struct arc_reg_desc *arc_reg,
+ const char * const type_name, const size_t type_name_len)
+{
+ assert(target);
+ assert(arc_reg);
+
+ struct arc_common *arc = target_to_arc(target);
+ assert(arc);
+
+ /* Find register type */
+ {
+ struct arc_reg_data_type *type;
+ list_for_each_entry(type, &arc->reg_data_types, list)
+ if (!strncmp(type->data_type.id, type_name, type_name_len)) {
+ arc_reg->data_type = &(type->data_type);
+ break;
+ }
+
+ if (!arc_reg->data_type)
+ return ERROR_ARC_REGTYPE_NOT_FOUND;
+ }
+
+ if (arc_reg->is_core) {
+ list_add_tail(&arc_reg->list, &arc->core_reg_descriptions);
+ arc->num_core_regs += 1;
+ } else if (arc_reg->is_bcr) {
+ list_add_tail(&arc_reg->list, &arc->bcr_reg_descriptions);
+ arc->num_bcr_regs += 1;
+ } else {
+ list_add_tail(&arc_reg->list, &arc->aux_reg_descriptions);
+ arc->num_aux_regs += 1;
+ }
+ arc->num_regs += 1;
+
+ LOG_DEBUG(
+ "added register {name=%s, num=0x%x, type=%s%s%s%s}",
+ arc_reg->name, arc_reg->arch_num, arc_reg->data_type->id,
+ arc_reg->is_core ? ", core" : "", arc_reg->is_bcr ? ", bcr" : "",
+ arc_reg->is_general ? ", general" : ""
+ );
+
+ return ERROR_OK;
+}
+
+/* Reading core or aux register */
+static int arc_get_register(struct reg *reg)
+{
+ assert(reg);
+
+ struct arc_reg_desc *desc = reg->arch_info;
+ struct target *target = desc->target;
+ struct arc_common *arc = target_to_arc(target);
+
+ uint32_t value;
+
+ if (reg->valid) {
+ LOG_DEBUG("Get register (cached) gdb_num=%" PRIu32 ", name=%s, value=0x%" PRIx32,
+ reg->number, desc->name, target_buffer_get_u32(target, reg->value));
+ return ERROR_OK;
+ }
+
+ if (desc->is_core) {
+ /* Accessing to R61/R62 registers causes Jtag hang */
+ if (desc->arch_num == CORE_R61_NUM || desc->arch_num == CORE_R62_NUM) {
+ LOG_ERROR("It is forbidden to read core registers 61 and 62.");
+ return ERROR_FAIL;
+ }
+ CHECK_RETVAL(arc_jtag_read_core_reg_one(&arc->jtag_info, desc->arch_num,
+ &value));
+ } else {
+ CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, desc->arch_num,
+ &value));
+ }
+
+ target_buffer_set_u32(target, reg->value, value);
+
+ /* If target is unhalted all register reads should be uncached. */
+ if (target->state == TARGET_HALTED)
+ reg->valid = true;
+ else
+ reg->valid = false;
+
+ reg->dirty = false;
+
+ LOG_DEBUG("Get register gdb_num=%" PRIu32 ", name=%s, value=0x%" PRIx32,
+ reg->number , desc->name, value);
+
+
+ return ERROR_OK;
+}
+
+/* Writing core or aux register */
+static int arc_set_register(struct reg *reg, uint8_t *buf)
+{
+ struct arc_reg_desc *desc = reg->arch_info;
+ struct target *target = desc->target;
+ uint32_t value = target_buffer_get_u32(target, buf);
+ /* Unlike "get" function "set" is supported only if target
+ * is in halt mode. Async writes are not supported yet. */
+ if (target->state != TARGET_HALTED)
+ return ERROR_TARGET_NOT_HALTED;
+
+ /* Accessing to R61/R62 registers causes Jtag hang */
+ if (desc->is_core && (desc->arch_num == CORE_R61_NUM ||
+ desc->arch_num == CORE_R62_NUM)) {
+ LOG_ERROR("It is forbidden to write core registers 61 and 62.");
+ return ERROR_FAIL;
+ }
+ target_buffer_set_u32(target, reg->value, value);
+
+ LOG_DEBUG("Set register gdb_num=%" PRIu32 ", name=%s, value=0x%08" PRIx32,
+ reg->number, desc->name, value);
+
+ reg->valid = true;
+ reg->dirty = true;
+
+ return ERROR_OK;
+}
+
+const struct reg_arch_type arc_reg_type = {
+ .get = arc_get_register,
+ .set = arc_set_register,
+};
+
+/* GDB register groups. For now we suport only general and "empty" */
+static const char * const reg_group_general = "general";
+static const char * const reg_group_other = "";
+
+/* Common code to initialize `struct reg` for different registers: core, aux, bcr. */
+static int arc_init_reg(struct target *target, struct reg *reg,
+ struct arc_reg_desc *reg_desc, unsigned long number)
+{
+ assert(target);
+ assert(reg);
+ assert(reg_desc);
+
+ struct arc_common *arc = target_to_arc(target);
+
+ /* Initialize struct reg */
+ reg->name = reg_desc->name;
+ reg->size = 32; /* All register in ARC are 32-bit */
+ reg->value = &reg_desc->reg_value;
+ reg->type = &arc_reg_type;
+ reg->arch_info = reg_desc;
+ reg->caller_save = true; /* @todo should be configurable. */
+ reg->reg_data_type = reg_desc->data_type;
+ reg->feature = &reg_desc->feature;
+
+ reg->feature->name = reg_desc->gdb_xml_feature;
+
+ /* reg->number is used by OpenOCD as value for @regnum. Thus when setting
+ * value of a register GDB will use it as a number of register in
+ * P-packet. OpenOCD gdbserver will then use number of register in
+ * P-packet as an array index in the reg_list returned by
+ * arc_regs_get_gdb_reg_list. So to ensure that registers are assigned
+ * correctly it would be required to either sort registers in
+ * arc_regs_get_gdb_reg_list or to assign numbers sequentially here and
+ * according to how registers will be sorted in
+ * arc_regs_get_gdb_reg_list. Second options is much more simpler. */
+ reg->number = number;
+
+ if (reg_desc->is_general) {
+ arc->last_general_reg = reg->number;
+ reg->group = reg_group_general;
+ } else {
+ reg->group = reg_group_other;
+ }
+
+ return ERROR_OK;
+}
+
+/* Building aux/core reg_cache */
+static int arc_build_reg_cache(struct target *target)
+{
+ unsigned long i = 0;
+ struct arc_reg_desc *reg_desc;
+ /* get pointers to arch-specific information */
+ struct arc_common *arc = target_to_arc(target);
+ const unsigned long num_regs = arc->num_core_regs + arc->num_aux_regs;
+ struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache);
+ struct reg_cache *cache = calloc(1, sizeof(*cache));
+ struct reg *reg_list = calloc(num_regs, sizeof(*reg_list));
+
+ if (!cache || !reg_list) {
+ LOG_ERROR("Not enough memory");
+ goto fail;
+ }
+
+ /* Build the process context cache */
+ cache->name = "arc registers";
+ cache->next = NULL;
+ cache->reg_list = reg_list;
+ cache->num_regs = num_regs;
+ arc->core_and_aux_cache = cache;
+ (*cache_p) = cache;
+
+ if (list_empty(&arc->core_reg_descriptions)) {
+ LOG_ERROR("No core registers were defined");
+ goto fail;
+ }
+
+ list_for_each_entry(reg_desc, &arc->core_reg_descriptions, list) {
+ CHECK_RETVAL(arc_init_reg(target, &reg_list[i], reg_desc, i));
+
+ LOG_DEBUG("reg n=%3li name=%3s group=%s feature=%s", i,
+ reg_list[i].name, reg_list[i].group,
+ reg_list[i].feature->name);
+
+ i += 1;
+ }
+
+ if (list_empty(&arc->aux_reg_descriptions)) {
+ LOG_ERROR("No aux registers were defined");
+ goto fail;
+ }
+
+ list_for_each_entry(reg_desc, &arc->aux_reg_descriptions, list) {
+ CHECK_RETVAL(arc_init_reg(target, &reg_list[i], reg_desc, i));
+
+ LOG_DEBUG("reg n=%3li name=%3s group=%s feature=%s", i,
+ reg_list[i].name, reg_list[i].group,
+ reg_list[i].feature->name);
+
+ /* PC and DEBUG are essential so we search for them. */
+ if (!strcmp("pc", reg_desc->name)) {
+ if (arc->pc_index_in_cache != ULONG_MAX) {
+ LOG_ERROR("Double definition of PC in configuration");
+ goto fail;
+ }
+ arc->pc_index_in_cache = i;
+ } else if (!strcmp("debug", reg_desc->name)) {
+ if (arc->debug_index_in_cache != ULONG_MAX) {
+ LOG_ERROR("Double definition of DEBUG in configuration");
+ goto fail;
+ }
+ arc->debug_index_in_cache = i;
+ }
+ i += 1;
+ }
+
+ if (arc->pc_index_in_cache == ULONG_MAX
+ || arc->debug_index_in_cache == ULONG_MAX) {
+ LOG_ERROR("`pc' and `debug' registers must be present in target description.");
+ goto fail;
+ }
+
+ assert(i == (arc->num_core_regs + arc->num_aux_regs));
+
+ arc->core_aux_cache_built = true;
+
+ return ERROR_OK;
+
+fail:
+ free(cache);
+ free(reg_list);
+
+ return ERROR_FAIL;
+}
+
+/* Build bcr reg_cache.
+ * This function must be called only after arc_build_reg_cache */
+static int arc_build_bcr_reg_cache(struct target *target)
+{
+ /* get pointers to arch-specific information */
+ struct arc_common *arc = target_to_arc(target);
+ const unsigned long num_regs = arc->num_bcr_regs;
+ struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache);
+ struct reg_cache *cache = malloc(sizeof(*cache));
+ struct reg *reg_list = calloc(num_regs, sizeof(*reg_list));
+
+ struct arc_reg_desc *reg_desc;
+ unsigned long i = 0;
+ unsigned long gdb_regnum = arc->core_and_aux_cache->num_regs;
+
+ if (!cache || !reg_list) {
+ LOG_ERROR("Unable to allocate memory");
+ goto fail;
+ }
+
+ /* Build the process context cache */
+ cache->name = "arc.bcr";
+ cache->next = NULL;
+ cache->reg_list = reg_list;
+ cache->num_regs = num_regs;
+ arc->bcr_cache = cache;
+ (*cache_p) = cache;
+
+ if (list_empty(&arc->bcr_reg_descriptions)) {
+ LOG_ERROR("No BCR registers are defined");
+ goto fail;
+ }
+
+ list_for_each_entry(reg_desc, &arc->bcr_reg_descriptions, list) {
+ CHECK_RETVAL(arc_init_reg(target, &reg_list[i], reg_desc, gdb_regnum));
+ /* BCRs always semantically, they are just read-as-zero, if there is
+ * not real register. */
+ reg_list[i].exist = true;
+
+ LOG_DEBUG("reg n=%3li name=%3s group=%s feature=%s", i,
+ reg_list[i].name, reg_list[i].group,
+ reg_list[i].feature->name);
+ i += 1;
+ gdb_regnum += 1;
+ }
+
+ assert(i == arc->num_bcr_regs);
+
+ arc->bcr_cache_built = true;
+
+
+ return ERROR_OK;
+fail:
+ free(cache);
+ free(reg_list);
+
+ return ERROR_FAIL;
+}
+
+
+static int arc_get_gdb_reg_list(struct target *target, struct reg **reg_list[],
+ int *reg_list_size, enum target_register_class reg_class)
+{
+ assert(target->reg_cache);
+ struct arc_common *arc = target_to_arc(target);
+
+ /* get pointers to arch-specific information storage */
+ *reg_list_size = arc->num_regs;
+ *reg_list = calloc(*reg_list_size, sizeof(struct reg *));
+
+ if (!*reg_list) {
+ LOG_ERROR("Unable to allocate memory");
+ return ERROR_FAIL;
+ }
+
+ /* OpenOCD gdb_server API seems to be inconsistent here: when it generates
+ * XML tdesc it filters out !exist registers, however when creating a
+ * g-packet it doesn't do so. REG_CLASS_ALL is used in first case, and
+ * REG_CLASS_GENERAL used in the latter one. Due to this we had to filter
+ * out !exist register for "general", but not for "all". Attempts to filter out
+ * !exist for "all" as well will cause a failed check in OpenOCD GDB
+ * server. */
+ if (reg_class == REG_CLASS_ALL) {
+ unsigned long i = 0;
+ struct reg_cache *reg_cache = target->reg_cache;
+ while (reg_cache) {
+ for (unsigned j = 0; j < reg_cache->num_regs; j++, i++)
+ (*reg_list)[i] = &reg_cache->reg_list[j];
+ reg_cache = reg_cache->next;
+ }
+ assert(i == arc->num_regs);
+ LOG_DEBUG("REG_CLASS_ALL: number of regs=%i", *reg_list_size);
+ } else {
+ unsigned long i = 0;
+ unsigned long gdb_reg_number = 0;
+ struct reg_cache *reg_cache = target->reg_cache;
+ while (reg_cache) {
+ for (unsigned j = 0;
+ j < reg_cache->num_regs && gdb_reg_number <= arc->last_general_reg;
+ j++) {
+ if (reg_cache->reg_list[j].exist) {
+ (*reg_list)[i] = &reg_cache->reg_list[j];
+ i++;
+ }
+ gdb_reg_number += 1;
+ }
+ reg_cache = reg_cache->next;
+ }
+ *reg_list_size = i;
+ LOG_DEBUG("REG_CLASS_GENERAL: number of regs=%i", *reg_list_size);
+ }
+
+ return ERROR_OK;
+}
+
+/* Reading field of struct_type register */
+int arc_reg_get_field(struct target *target, const char *reg_name,
+ const char *field_name, uint32_t *value_ptr)
+{
+ struct reg_data_type_struct_field *field;
+
+ LOG_DEBUG("getting register field (reg_name=%s, field_name=%s)", reg_name, field_name);
+
+ /* Get register */
+ struct reg *reg = arc_reg_get_by_name(target->reg_cache, reg_name, true);
+
+ if (!reg) {
+ LOG_ERROR("Requested register `%s' doens't exist.", reg_name);
+ return ERROR_ARC_REGISTER_NOT_FOUND;
+ }
+
+ if (reg->reg_data_type->type != REG_TYPE_ARCH_DEFINED
+ || reg->reg_data_type->type_class != REG_TYPE_CLASS_STRUCT)
+ return ERROR_ARC_REGISTER_IS_NOT_STRUCT;
+
+ /* Get field in a register */
+ struct reg_data_type_struct *reg_struct =
+ reg->reg_data_type->reg_type_struct;
+ for (field = reg_struct->fields;
+ field;
+ field = field->next) {
+ if (!strcmp(field->name, field_name))
+ break;
+ }
+
+ if (!field)
+ return ERROR_ARC_REGISTER_FIELD_NOT_FOUND;
+
+ if (!field->use_bitfields)
+ return ERROR_ARC_FIELD_IS_NOT_BITFIELD;
+
+ if (!reg->valid)
+ CHECK_RETVAL(reg->type->get(reg));
+
+ /* First do endiannes-safe read of register value
+ * then convert it to binary buffer for further
+ * field extraction */
+
+ *value_ptr = buf_get_u32(reg->value, field->bitfield->start,
+ field->bitfield->end - field->bitfield->start + 1);
+
+ return ERROR_OK;
+}
+
+static int arc_get_register_value(struct target *target, const char *reg_name,
+ uint32_t *value_ptr)
+{
+ LOG_DEBUG("reg_name=%s", reg_name);
+
+ struct reg *reg = arc_reg_get_by_name(target->reg_cache, reg_name, true);
+
+ if (!reg)
+ return ERROR_ARC_REGISTER_NOT_FOUND;
+
+ if (!reg->valid)
+ CHECK_RETVAL(reg->type->get(reg));
+
+ *value_ptr = target_buffer_get_u32(target, reg->value);
+
+ return ERROR_OK;
+}
+
+
+/* Configure DCCM's */
+static int arc_configure_dccm(struct target *target)
+{
+ struct arc_common *arc = target_to_arc(target);
+
+ uint32_t dccm_build_version, dccm_build_size0, dccm_build_size1;
+ CHECK_RETVAL(arc_reg_get_field(target, "dccm_build", "version",
+ &dccm_build_version));
+ CHECK_RETVAL(arc_reg_get_field(target, "dccm_build", "size0",
+ &dccm_build_size0));
+ CHECK_RETVAL(arc_reg_get_field(target, "dccm_build", "size1",
+ &dccm_build_size1));
+ /* There is no yet support of configurable number of cycles,
+ * So there is no difference between v3 and v4 */
+ if ((dccm_build_version == 3 || dccm_build_version == 4) && dccm_build_size0 > 0) {
+ CHECK_RETVAL(arc_get_register_value(target, "aux_dccm", &(arc->dccm_start)));
+ uint32_t dccm_size = 0x100;
+ dccm_size <<= dccm_build_size0;
+ if (dccm_build_size0 == 0xF)
+ dccm_size <<= dccm_build_size1;
+ arc->dccm_end = arc->dccm_start + dccm_size;
+ LOG_DEBUG("DCCM detected start=0x%" PRIx32 " end=0x%" PRIx32,
+ arc->dccm_start, arc->dccm_end);
+
+ }
+ return ERROR_OK;
+}
+
+
+/* Configure ICCM's */
+
+static int arc_configure_iccm(struct target *target)
+{
+ struct arc_common *arc = target_to_arc(target);
+
+ /* ICCM0 */
+ uint32_t iccm_build_version, iccm_build_size00, iccm_build_size01;
+ uint32_t aux_iccm = 0;
+ CHECK_RETVAL(arc_reg_get_field(target, "iccm_build", "version",
+ &iccm_build_version));
+ CHECK_RETVAL(arc_reg_get_field(target, "iccm_build", "iccm0_size0",
+ &iccm_build_size00));
+ CHECK_RETVAL(arc_reg_get_field(target, "iccm_build", "iccm0_size1",
+ &iccm_build_size01));
+ if (iccm_build_version == 4 && iccm_build_size00 > 0) {
+ CHECK_RETVAL(arc_get_register_value(target, "aux_iccm", &aux_iccm));
+ uint32_t iccm0_size = 0x100;
+ iccm0_size <<= iccm_build_size00;
+ if (iccm_build_size00 == 0xF)
+ iccm0_size <<= iccm_build_size01;
+ /* iccm0 start is located in highest 4 bits of aux_iccm */
+ arc->iccm0_start = aux_iccm & 0xF0000000;
+ arc->iccm0_end = arc->iccm0_start + iccm0_size;
+ LOG_DEBUG("ICCM0 detected start=0x%" PRIx32 " end=0x%" PRIx32,
+ arc->iccm0_start, arc->iccm0_end);
+ }
+
+ /* ICCM1 */
+ uint32_t iccm_build_size10, iccm_build_size11;
+ CHECK_RETVAL(arc_reg_get_field(target, "iccm_build", "iccm1_size0",
+ &iccm_build_size10));
+ CHECK_RETVAL(arc_reg_get_field(target, "iccm_build", "iccm1_size1",
+ &iccm_build_size11));
+ if (iccm_build_version == 4 && iccm_build_size10 > 0) {
+ /* Use value read for ICCM0 */
+ if (!aux_iccm)
+ CHECK_RETVAL(arc_get_register_value(target, "aux_iccm", &aux_iccm));
+ uint32_t iccm1_size = 0x100;
+ iccm1_size <<= iccm_build_size10;
+ if (iccm_build_size10 == 0xF)
+ iccm1_size <<= iccm_build_size11;
+ arc->iccm1_start = aux_iccm & 0x0F000000;
+ arc->iccm1_end = arc->iccm1_start + iccm1_size;
+ LOG_DEBUG("ICCM1 detected start=0x%" PRIx32 " end=0x%" PRIx32,
+ arc->iccm1_start, arc->iccm1_end);
+ }
+ return ERROR_OK;
+}
+
+/* Configure some core features, depending on BCRs. */
+static int arc_configure(struct target *target)
+{
+ LOG_DEBUG("Configuring ARC ICCM and DCCM");
+
+ /* Configuring DCCM if DCCM_BUILD and AUX_DCCM are known registers. */
+ if (arc_reg_get_by_name(target->reg_cache, "dccm_build", true) &&
+ arc_reg_get_by_name(target->reg_cache, "aux_dccm", true))
+ CHECK_RETVAL(arc_configure_dccm(target));
+
+ /* Configuring ICCM if ICCM_BUILD and AUX_ICCM are known registers. */
+ if (arc_reg_get_by_name(target->reg_cache, "iccm_build", true) &&
+ arc_reg_get_by_name(target->reg_cache, "aux_iccm", true))
+ CHECK_RETVAL(arc_configure_iccm(target));
+
+ return ERROR_OK;
+}
+
+/* arc_examine is function, which is used for all arc targets*/
+static int arc_examine(struct target *target)
+{
+ uint32_t status;
+ struct arc_common *arc = target_to_arc(target);
+
+ CHECK_RETVAL(arc_jtag_startup(&arc->jtag_info));
+
+ if (!target_was_examined(target)) {
+ CHECK_RETVAL(arc_jtag_status(&arc->jtag_info, &status));
+ if (status & ARC_JTAG_STAT_RU)
+ target->state = TARGET_RUNNING;
+ else
+ target->state = TARGET_HALTED;
+
+ /* Read BCRs and configure optional registers. */
+ CHECK_RETVAL(arc_configure(target));
+
+ target_set_examined(target);
+ }
+
+ return ERROR_OK;
+}
+
+static int arc_halt(struct target *target)
+{
+ uint32_t value, irq_state;
+ struct arc_common *arc = target_to_arc(target);
+
+ LOG_DEBUG("target->state: %s", target_state_name(target));
+
+ if (target->state == TARGET_HALTED) {
+ LOG_DEBUG("target was already halted");
+ return ERROR_OK;
+ }
+
+ if (target->state == TARGET_UNKNOWN)
+ LOG_WARNING("target was in unknown state when halt was requested");
+
+ if (target->state == TARGET_RESET) {
+ if ((jtag_get_reset_config() & RESET_SRST_PULLS_TRST) && jtag_get_srst()) {
+ LOG_ERROR("can't request a halt while in reset if nSRST pulls nTRST");
+ return ERROR_TARGET_FAILURE;
+ } else {
+ target->debug_reason = DBG_REASON_DBGRQ;
+ }
+ }
+
+ /* Break (stop) processor.
+ * Do read-modify-write sequence, or DEBUG.UB will be reset unintentionally.
+ * We do not use here arc_get/set_core_reg functions here because they imply
+ * that the processor is already halted. */
+ CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, AUX_DEBUG_REG, &value));
+ value |= SET_CORE_FORCE_HALT; /* set the HALT bit */
+ CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_DEBUG_REG, value));
+ alive_sleep(1);
+
+ /* Save current IRQ state */
+ CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, AUX_STATUS32_REG, &irq_state));
+
+ if (irq_state & AUX_STATUS32_REG_IE_BIT)
+ arc->irq_state = 1;
+ else
+ arc->irq_state = 0;
+
+ /* update state and notify gdb*/
+ target->state = TARGET_HALTED;
+ CHECK_RETVAL(target_call_event_callbacks(target, TARGET_EVENT_HALTED));
+
+ /* some more debug information */
+ if (debug_level >= LOG_LVL_DEBUG) {
+ LOG_DEBUG("core stopped (halted) DEGUB-REG: 0x%08" PRIx32, value);
+ CHECK_RETVAL(arc_get_register_value(target, "status32", &value));
+ LOG_DEBUG("core STATUS32: 0x%08" PRIx32, value);
+ }
+
+ return ERROR_OK;
+}
+
+/**
+ * Read registers that are used in GDB g-packet. We don't read them one-by-one,
+ * but do that in one batch operation to improve speed. Calls to JTAG layer are
+ * expensive so it is better to make one big call that reads all necessary
+ * registers, instead of many calls, one for one register.
+ */
+static int arc_save_context(struct target *target)
+{
+ int retval = ERROR_OK;
+ unsigned int i;
+ struct arc_common *arc = target_to_arc(target);
+ struct reg *reg_list = arc->core_and_aux_cache->reg_list;
+
+ LOG_DEBUG("Saving aux and core registers values");
+ assert(reg_list);
+
+ /* It is assumed that there is at least one AUX register in the list, for
+ * example PC. */
+ const uint32_t core_regs_size = arc->num_core_regs * sizeof(uint32_t);
+ /* last_general_reg is inclusive number. To get count of registers it is
+ * required to do +1. */
+ const uint32_t regs_to_scan =
+ MIN(arc->last_general_reg + 1, arc->num_regs);
+ const uint32_t aux_regs_size = arc->num_aux_regs * sizeof(uint32_t);
+ uint32_t *core_values = malloc(core_regs_size);
+ uint32_t *aux_values = malloc(aux_regs_size);
+ uint32_t *core_addrs = malloc(core_regs_size);
+ uint32_t *aux_addrs = malloc(aux_regs_size);
+ unsigned int core_cnt = 0;
+ unsigned int aux_cnt = 0;
+
+ if (!core_values || !core_addrs || !aux_values || !aux_addrs) {
+ LOG_ERROR("Unable to allocate memory");
+ retval = ERROR_FAIL;
+ goto exit;
+ }
+
+ memset(core_values, 0xff, core_regs_size);
+ memset(core_addrs, 0xff, core_regs_size);
+ memset(aux_values, 0xff, aux_regs_size);
+ memset(aux_addrs, 0xff, aux_regs_size);
+
+ for (i = 0; i < MIN(arc->num_core_regs, regs_to_scan); i++) {
+ struct reg *reg = &(reg_list[i]);
+ struct arc_reg_desc *arc_reg = reg->arch_info;
+ if (!reg->valid && reg->exist) {
+ core_addrs[core_cnt] = arc_reg->arch_num;
+ core_cnt += 1;
+ }
+ }
+
+ for (i = arc->num_core_regs; i < regs_to_scan; i++) {
+ struct reg *reg = &(reg_list[i]);
+ struct arc_reg_desc *arc_reg = reg->arch_info;
+ if (!reg->valid && reg->exist) {
+ aux_addrs[aux_cnt] = arc_reg->arch_num;
+ aux_cnt += 1;
+ }
+ }
+
+ /* Read data from target. */
+ if (core_cnt > 0) {
+ retval = arc_jtag_read_core_reg(&arc->jtag_info, core_addrs, core_cnt, core_values);
+ if (ERROR_OK != retval) {
+ LOG_ERROR("Attempt to read core registers failed.");
+ retval = ERROR_FAIL;
+ goto exit;
+ }
+ }
+ if (aux_cnt > 0) {
+ retval = arc_jtag_read_aux_reg(&arc->jtag_info, aux_addrs, aux_cnt, aux_values);
+ if (ERROR_OK != retval) {
+ LOG_ERROR("Attempt to read aux registers failed.");
+ retval = ERROR_FAIL;
+ goto exit;
+ }
+ }
+
+ /* Parse core regs */
+ core_cnt = 0;
+ for (i = 0; i < MIN(arc->num_core_regs, regs_to_scan); i++) {
+ struct reg *reg = &(reg_list[i]);
+ struct arc_reg_desc *arc_reg = reg->arch_info;
+ if (!reg->valid && reg->exist) {
+ target_buffer_set_u32(target, reg->value, core_values[core_cnt]);
+ core_cnt += 1;
+ reg->valid = true;
+ reg->dirty = false;
+ LOG_DEBUG("Get core register regnum=%" PRIu32 ", name=%s, value=0x%08" PRIx32,
+ i, arc_reg->name, core_values[core_cnt]);
+ }
+ }
+
+ /* Parse aux regs */
+ aux_cnt = 0;
+ for (i = arc->num_core_regs; i < regs_to_scan; i++) {
+ struct reg *reg = &(reg_list[i]);
+ struct arc_reg_desc *arc_reg = reg->arch_info;
+ if (!reg->valid && reg->exist) {
+ target_buffer_set_u32(target, reg->value, aux_values[aux_cnt]);
+ aux_cnt += 1;
+ reg->valid = true;
+ reg->dirty = false;
+ LOG_DEBUG("Get aux register regnum=%" PRIu32 ", name=%s, value=0x%08" PRIx32,
+ i , arc_reg->name, aux_values[aux_cnt]);
+ }
+ }
+
+exit:
+ free(core_values);
+ free(core_addrs);
+ free(aux_values);
+ free(aux_addrs);
+
+ return retval;
+}
+
+static int arc_examine_debug_reason(struct target *target)
+{
+ uint32_t debug_bh;
+
+ /* Only check for reason if don't know it already. */
+ /* BTW After singlestep at this point core is not marked as halted, so
+ * reading from memory to get current instruction wouldn't work anyway. */
+ if (target->debug_reason == DBG_REASON_DBGRQ ||
+ target->debug_reason == DBG_REASON_SINGLESTEP) {
+ return ERROR_OK;
+ }
+
+ CHECK_RETVAL(arc_reg_get_field(target, "debug", "bh",
+ &debug_bh));
+
+ if (debug_bh) {
+ /* DEBUG.BH is set if core halted due to BRK instruction. */
+ target->debug_reason = DBG_REASON_BREAKPOINT;
+ } else {
+ /* TODO: Add Actionpoint check when AP support will be introduced*/
+ LOG_WARNING("Unknown debug reason");
+ }
+
+ return ERROR_OK;
+}
+
+static int arc_debug_entry(struct target *target)
+{
+ CHECK_RETVAL(arc_save_context(target));
+
+ /* TODO: reset internal indicators of caches states, otherwise D$/I$
+ * will not be flushed/invalidated when required. */
+ CHECK_RETVAL(arc_examine_debug_reason(target));
+
+ return ERROR_OK;
+}
+
+static int arc_poll(struct target *target)
+{
+ uint32_t status, value;
+ struct arc_common *arc = target_to_arc(target);
+
+ /* gdb calls continuously through this arc_poll() function */
+ CHECK_RETVAL(arc_jtag_status(&arc->jtag_info, &status));
+
+ /* check for processor halted */
+ if (status & ARC_JTAG_STAT_RU) {
+ if (target->state != TARGET_RUNNING) {
+ LOG_WARNING("target is still running!");
+ target->state = TARGET_RUNNING;
+ }
+ return ERROR_OK;
+ }
+ /* In some cases JTAG status register indicates that
+ * processor is in halt mode, but processor is still running.
+ * We check halt bit of AUX STATUS32 register for setting correct state. */
+ if ((target->state == TARGET_RUNNING) || (target->state == TARGET_RESET)) {
+ CHECK_RETVAL(arc_get_register_value(target, "status32", &value));
+ if (value & AUX_STATUS32_REG_HALT_BIT) {
+ LOG_DEBUG("ARC core in halt or reset state.");
+ target->state = TARGET_HALTED;
+ CHECK_RETVAL(arc_debug_entry(target));
+ CHECK_RETVAL(target_call_event_callbacks(target, TARGET_EVENT_HALTED));
+ } else {
+ LOG_DEBUG("Discrepancy of STATUS32[0] HALT bit and ARC_JTAG_STAT_RU, "
+ "target is still running");
+ }
+
+ } else if (target->state == TARGET_DEBUG_RUNNING) {
+
+ target->state = TARGET_HALTED;
+ LOG_DEBUG("ARC core is in debug running mode");
+
+ CHECK_RETVAL(arc_debug_entry(target));
+
+ CHECK_RETVAL(target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED));
+ }
+
+ return ERROR_OK;
+}
+
+static int arc_assert_reset(struct target *target)
+{
+ struct arc_common *arc = target_to_arc(target);
+ enum reset_types jtag_reset_config = jtag_get_reset_config();
+ bool srst_asserted = false;
+
+ LOG_DEBUG("target->state: %s", target_state_name(target));
+
+ if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) {
+ /* allow scripts to override the reset event */
+
+ target_handle_event(target, TARGET_EVENT_RESET_ASSERT);
+ register_cache_invalidate(arc->core_and_aux_cache);
+ /* An ARC target might be in halt state after reset, so
+ * if script requested processor to resume, then it must
+ * be manually started to ensure that this request
+ * is satisfied. */
+ if (target->state == TARGET_HALTED && !target->reset_halt) {
+ /* Resume the target and continue from the current
+ * PC register value. */
+ LOG_DEBUG("Starting CPU execution after reset");
+ CHECK_RETVAL(target_resume(target, 1, 0, 0, 0));
+ }
+ target->state = TARGET_RESET;
+
+ return ERROR_OK;
+ }
+
+ /* some cores support connecting while srst is asserted
+ * use that mode if it has been configured */
+ if (!(jtag_reset_config & RESET_SRST_PULLS_TRST) &&
+ (jtag_reset_config & RESET_SRST_NO_GATING)) {
+ jtag_add_reset(0, 1);
+ srst_asserted = true;
+ }
+
+ if (jtag_reset_config & RESET_HAS_SRST) {
+ /* should issue a srst only, but we may have to assert trst as well */
+ if (jtag_reset_config & RESET_SRST_PULLS_TRST)
+ jtag_add_reset(1, 1);
+ else if (!srst_asserted)
+ jtag_add_reset(0, 1);
+ }
+
+ target->state = TARGET_RESET;
+ jtag_add_sleep(50000);
+
+ register_cache_invalidate(arc->core_and_aux_cache);
+
+ if (target->reset_halt)
+ CHECK_RETVAL(target_halt(target));
+
+ return ERROR_OK;
+}
+
+static int arc_deassert_reset(struct target *target)
+{
+ LOG_DEBUG("target->state: %s", target_state_name(target));
+
+ /* deassert reset lines */
+ jtag_add_reset(0, 0);
+
+ return ERROR_OK;
+}
+
+static int arc_arch_state(struct target *target)
+{
+ uint32_t pc_value;
+
+ if (debug_level < LOG_LVL_DEBUG)
+ return ERROR_OK;
+
+ CHECK_RETVAL(arc_get_register_value(target, "pc", &pc_value));
+
+ LOG_DEBUG("target state: %s; PC at: 0x%08" PRIx32,
+ target_state_name(target),
+ pc_value);
+
+ return ERROR_OK;
+}
+
+/**
+ * See arc_save_context() for reason why we want to dump all regs at once.
+ * This however means that if there are dependencies between registers they
+ * will not be observable until target will be resumed.
+ */
+static int arc_restore_context(struct target *target)
+{
+ int retval = ERROR_OK;
+ unsigned int i;
+ struct arc_common *arc = target_to_arc(target);
+ struct reg *reg_list = arc->core_and_aux_cache->reg_list;
+
+ LOG_DEBUG("Restoring registers values");
+ assert(reg_list);
+
+ const uint32_t core_regs_size = arc->num_core_regs * sizeof(uint32_t);
+ const uint32_t aux_regs_size = arc->num_aux_regs * sizeof(uint32_t);
+ uint32_t *core_values = malloc(core_regs_size);
+ uint32_t *aux_values = malloc(aux_regs_size);
+ uint32_t *core_addrs = malloc(core_regs_size);
+ uint32_t *aux_addrs = malloc(aux_regs_size);
+ unsigned int core_cnt = 0;
+ unsigned int aux_cnt = 0;
+
+ if (!core_values || !core_addrs || !aux_values || !aux_addrs) {
+ LOG_ERROR("Unable to allocate memory");
+ retval = ERROR_FAIL;
+ goto exit;
+ }
+
+ memset(core_values, 0xff, core_regs_size);
+ memset(core_addrs, 0xff, core_regs_size);
+ memset(aux_values, 0xff, aux_regs_size);
+ memset(aux_addrs, 0xff, aux_regs_size);
+
+ for (i = 0; i < arc->num_core_regs; i++) {
+ struct reg *reg = &(reg_list[i]);
+ struct arc_reg_desc *arc_reg = reg->arch_info;
+ if (reg->valid && reg->exist && reg->dirty) {
+ LOG_DEBUG("Will write regnum=%u", i);
+ core_addrs[core_cnt] = arc_reg->arch_num;
+ core_values[core_cnt] = target_buffer_get_u32(target, reg->value);
+ core_cnt += 1;
+ }
+ }
+
+ for (i = 0; i < arc->num_aux_regs; i++) {
+ struct reg *reg = &(reg_list[arc->num_core_regs + i]);
+ struct arc_reg_desc *arc_reg = reg->arch_info;
+ if (reg->valid && reg->exist && reg->dirty) {
+ LOG_DEBUG("Will write regnum=%lu", arc->num_core_regs + i);
+ aux_addrs[aux_cnt] = arc_reg->arch_num;
+ aux_values[aux_cnt] = target_buffer_get_u32(target, reg->value);
+ aux_cnt += 1;
+ }
+ }
+
+ /* Write data to target.
+ * Check before write, if aux and core count is greater than 0. */
+ if (core_cnt > 0) {
+ retval = arc_jtag_write_core_reg(&arc->jtag_info, core_addrs, core_cnt, core_values);
+ if (ERROR_OK != retval) {
+ LOG_ERROR("Attempt to write to core registers failed.");
+ retval = ERROR_FAIL;
+ goto exit;
+ }
+ }
+
+ if (aux_cnt > 0) {
+ retval = arc_jtag_write_aux_reg(&arc->jtag_info, aux_addrs, aux_cnt, aux_values);
+ if (ERROR_OK != retval) {
+ LOG_ERROR("Attempt to write to aux registers failed.");
+ retval = ERROR_FAIL;
+ goto exit;
+ }
+ }
+
+exit:
+ free(core_values);
+ free(core_addrs);
+ free(aux_values);
+ free(aux_addrs);
+
+ return retval;
+}
+
+static int arc_enable_interrupts(struct target *target, int enable)
+{
+ uint32_t value;
+
+ struct arc_common *arc = target_to_arc(target);
+
+ CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, AUX_STATUS32_REG, &value));
+
+ if (enable) {
+ /* enable interrupts */
+ value |= SET_CORE_ENABLE_INTERRUPTS;
+ CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_STATUS32_REG, value));
+ LOG_DEBUG("interrupts enabled");
+ } else {
+ /* disable interrupts */
+ value &= ~SET_CORE_ENABLE_INTERRUPTS;
+ CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_STATUS32_REG, value));
+ LOG_DEBUG("interrupts disabled");
+ }
+
+ return ERROR_OK;
+}
+
+static int arc_resume(struct target *target, int current, target_addr_t address,
+ int handle_breakpoints, int debug_execution)
+{
+ struct arc_common *arc = target_to_arc(target);
+ uint32_t resume_pc = 0;
+ uint32_t value;
+ struct reg *pc = &arc->core_and_aux_cache->reg_list[arc->pc_index_in_cache];
+
+ LOG_DEBUG("current:%i, address:0x%08" TARGET_PRIxADDR ", handle_breakpoints(not supported yet):%i,"
+ " debug_execution:%i", current, address, handle_breakpoints, debug_execution);
+
+ if (target->state != TARGET_HALTED) {
+ LOG_WARNING("target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* current = 1: continue on current PC, otherwise continue at <address> */
+ if (!current) {
+ target_buffer_set_u32(target, pc->value, address);
+ pc->dirty = 1;
+ pc->valid = 1;
+ LOG_DEBUG("Changing the value of current PC to 0x%08" TARGET_PRIxADDR, address);
+ }
+
+ if (!current)
+ resume_pc = address;
+ else
+ resume_pc = target_buffer_get_u32(target, pc->value);
+
+ CHECK_RETVAL(arc_restore_context(target));
+
+ LOG_DEBUG("Target resumes from PC=0x%" PRIx32 ", pc.dirty=%i, pc.valid=%i",
+ resume_pc, pc->dirty, pc->valid);
+
+ /* check if GDB tells to set our PC where to continue from */
+ if ((pc->valid == 1) && (resume_pc == target_buffer_get_u32(target, pc->value))) {
+ value = target_buffer_get_u32(target, pc->value);
+ LOG_DEBUG("resume Core (when start-core) with PC @:0x%08" PRIx32, value);
+ CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_PC_REG, value));
+ }
+
+ /* Restore IRQ state if not in debug_execution*/
+ if (!debug_execution)
+ CHECK_RETVAL(arc_enable_interrupts(target, arc->irq_state));
+ else
+ CHECK_RETVAL(arc_enable_interrupts(target, !debug_execution));
+
+ target->debug_reason = DBG_REASON_NOTHALTED;
+
+ /* ready to get us going again */
+ target->state = TARGET_RUNNING;
+ CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, AUX_STATUS32_REG, &value));
+ value &= ~SET_CORE_HALT_BIT; /* clear the HALT bit */
+ CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, AUX_STATUS32_REG, value));
+ LOG_DEBUG("Core started to run");
+
+ /* registers are now invalid */
+ register_cache_invalidate(arc->core_and_aux_cache);
+
+ if (!debug_execution) {
+ target->state = TARGET_RUNNING;
+ CHECK_RETVAL(target_call_event_callbacks(target, TARGET_EVENT_RESUMED));
+ LOG_DEBUG("target resumed at 0x%08" PRIx32, resume_pc);
+ } else {
+ target->state = TARGET_DEBUG_RUNNING;
+ CHECK_RETVAL(target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED));
+ LOG_DEBUG("target debug resumed at 0x%08" PRIx32, resume_pc);
+ }
+
+ return ERROR_OK;
+}
+
+static int arc_init_target(struct command_context *cmd_ctx, struct target *target)
+{
+ CHECK_RETVAL(arc_build_reg_cache(target));
+ CHECK_RETVAL(arc_build_bcr_reg_cache(target));
+ target->debug_reason = DBG_REASON_DBGRQ;
+ return ERROR_OK;
+}
+
+static void arc_free_reg_cache(struct reg_cache *cache)
+{
+ free(cache->reg_list);
+ free(cache);
+}
+
+static void arc_deinit_target(struct target *target)
+{
+ struct arc_common *arc = target_to_arc(target);
+
+ LOG_DEBUG("deinitialization of target");
+ if (arc->core_aux_cache_built)
+ arc_free_reg_cache(arc->core_and_aux_cache);
+ if (arc->bcr_cache_built)
+ arc_free_reg_cache(arc->bcr_cache);
+
+ struct arc_reg_data_type *type, *n;
+ struct arc_reg_desc *desc, *k;
+
+ /* Free arc-specific reg_data_types allocations*/
+ list_for_each_entry_safe_reverse(type, n, &arc->reg_data_types, list) {
+ if (type->data_type.type_class == REG_TYPE_CLASS_STRUCT) {
+ free(type->data_type.reg_type_struct->fields);
+ free(type->bitfields);
+ free(type);
+ } else if (type->data_type.type_class == REG_TYPE_CLASS_FLAGS) {
+ free(type->data_type.reg_type_flags->fields);
+ free(type->bitfields);
+ free(type);
+ }
+ }
+
+ /* Free standard_gdb_types reg_data_types allocations */
+ type = list_first_entry(&arc->reg_data_types, struct arc_reg_data_type, list);
+ free(type);
+
+ list_for_each_entry_safe(desc, k, &arc->aux_reg_descriptions, list)
+ free_reg_desc(desc);
+
+ list_for_each_entry_safe(desc, k, &arc->core_reg_descriptions, list)
+ free_reg_desc(desc);
+
+ list_for_each_entry_safe(desc, k, &arc->bcr_reg_descriptions, list)
+ free_reg_desc(desc);
+
+ free(arc);
+}
+
+
+static int arc_target_create(struct target *target, Jim_Interp *interp)
+{
+ struct arc_common *arc = calloc(1, sizeof(*arc));
+
+ if (!arc) {
+ LOG_ERROR("Unable to allocate memory");
+ return ERROR_FAIL;
+ }
+
+ LOG_DEBUG("Entering");
+ CHECK_RETVAL(arc_init_arch_info(target, arc, target->tap));
+
+ return ERROR_OK;
+}
+
+
+/* ARC v2 target */
+struct target_type arcv2_target = {
+ .name = "arcv2",
+
+ .poll = arc_poll,
+
+ .arch_state = arc_arch_state,
+
+ /* TODO That seems like something similiar to metaware hostlink, so perhaps
+ * we can exploit this in the future. */
+ .target_request_data = NULL,
+
+ .halt = arc_halt,
+ .resume = arc_resume,
+ .step = NULL,
+
+ .assert_reset = arc_assert_reset,
+ .deassert_reset = arc_deassert_reset,
+
+ /* TODO Implement soft_reset_halt */
+ .soft_reset_halt = NULL,
+
+ .get_gdb_reg_list = arc_get_gdb_reg_list,
+
+ .read_memory = arc_mem_read,
+ .write_memory = arc_mem_write,
+ .checksum_memory = NULL,
+ .blank_check_memory = NULL,
+
+ .add_breakpoint = NULL,
+ .add_context_breakpoint = NULL,
+ .add_hybrid_breakpoint = NULL,
+ .remove_breakpoint = NULL,
+ .add_watchpoint = NULL,
+ .remove_watchpoint = NULL,
+ .hit_watchpoint = NULL,
+
+ .run_algorithm = NULL,
+ .start_algorithm = NULL,
+ .wait_algorithm = NULL,
+
+ .commands = arc_monitor_command_handlers,
+
+ .target_create = arc_target_create,
+ .init_target = arc_init_target,
+ .deinit_target = arc_deinit_target,
+ .examine = arc_examine,
+
+ .virt2phys = NULL,
+ .read_phys_memory = NULL,
+ .write_phys_memory = NULL,
+ .mmu = NULL,
+};
diff --git a/src/target/arc.h b/src/target/arc.h
new file mode 100644
index 0000000..311648e
--- /dev/null
+++ b/src/target/arc.h
@@ -0,0 +1,212 @@
+/***************************************************************************
+ * Copyright (C) 2013-2015,2019-2020 Synopsys, Inc. *
+ * Frank Dols <frank.dols@synopsys.com> *
+ * Mischa Jonker <mischa.jonker@synopsys.com> *
+ * Anton Kolesov <anton.kolesov@synopsys.com> *
+ * Evgeniy Didin <didin@synopsys.com> *
+ * *
+ * SPDX-License-Identifier: GPL-2.0-or-later *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ARC_H
+#define OPENOCD_TARGET_ARC_H
+
+#include <helper/time_support.h>
+#include <jtag/jtag.h>
+
+#include "algorithm.h"
+#include "breakpoints.h"
+#include "jtag/interface.h"
+#include "register.h"
+#include "target.h"
+#include "target_request.h"
+#include "target_type.h"
+#include "helper/bits.h"
+
+#include "arc_jtag.h"
+#include "arc_cmd.h"
+#include "arc_mem.h"
+
+#define ARC_COMMON_MAGIC 0xB32EB324 /* just a unique number */
+
+#define AUX_DEBUG_REG 0x5
+#define AUX_PC_REG 0x6
+#define AUX_STATUS32_REG 0xA
+
+#define SET_CORE_FORCE_HALT BIT(1)
+#define SET_CORE_HALT_BIT BIT(0) /* STATUS32[0] = H field */
+#define SET_CORE_ENABLE_INTERRUPTS BIT(31)
+
+#define AUX_STATUS32_REG_HALT_BIT BIT(0)
+#define AUX_STATUS32_REG_IE_BIT BIT(31) /* STATUS32[31] = IE field */
+
+/* Reserved core registers */
+#define CORE_R61_NUM (61)
+#define CORE_R62_NUM (62)
+
+#define CORE_REG_MAX_NUMBER (63)
+
+/* Limit reg_type/reg_type_field name to 20 symbols */
+#define REG_TYPE_MAX_NAME_LENGTH 20
+
+struct arc_reg_bitfield {
+ struct reg_data_type_bitfield bitfield;
+ char name[REG_TYPE_MAX_NAME_LENGTH];
+};
+/* Register data type */
+struct arc_reg_data_type {
+ struct list_head list;
+ struct reg_data_type data_type;
+ struct reg_data_type_flags data_type_flags;
+ struct reg_data_type_struct data_type_struct;
+ char data_type_id[REG_TYPE_MAX_NAME_LENGTH];
+ struct arc_reg_bitfield *bitfields;
+};
+
+
+
+/* Standard GDB register types */
+static const struct reg_data_type standard_gdb_types[] = {
+ { .type = REG_TYPE_INT, .id = "int" },
+ { .type = REG_TYPE_INT8, .id = "int8" },
+ { .type = REG_TYPE_INT16, .id = "int16" },
+ { .type = REG_TYPE_INT32, .id = "int32" },
+ { .type = REG_TYPE_INT64, .id = "int64" },
+ { .type = REG_TYPE_INT128, .id = "int128" },
+ { .type = REG_TYPE_UINT8, .id = "uint8" },
+ { .type = REG_TYPE_UINT16, .id = "uint16" },
+ { .type = REG_TYPE_UINT32, .id = "uint32" },
+ { .type = REG_TYPE_UINT64, .id = "uint64" },
+ { .type = REG_TYPE_UINT128, .id = "uint128" },
+ { .type = REG_TYPE_CODE_PTR, .id = "code_ptr" },
+ { .type = REG_TYPE_DATA_PTR, .id = "data_ptr" },
+ { .type = REG_TYPE_FLOAT, .id = "float" },
+ { .type = REG_TYPE_IEEE_SINGLE, .id = "ieee_single" },
+ { .type = REG_TYPE_IEEE_DOUBLE, .id = "ieee_double" },
+};
+
+
+struct arc_common {
+ uint32_t common_magic;
+
+ struct arc_jtag jtag_info;
+
+ struct reg_cache *core_and_aux_cache;
+ struct reg_cache *bcr_cache;
+
+ /* Indicate if cach was built (for deinit function) */
+ bool core_aux_cache_built;
+ bool bcr_cache_built;
+ /* Closely Coupled memory(CCM) regions for performance-critical
+ * code (optional). */
+ uint32_t iccm0_start;
+ uint32_t iccm0_end;
+ uint32_t iccm1_start;
+ uint32_t iccm1_end;
+ uint32_t dccm_start;
+ uint32_t dccm_end;
+
+ int irq_state;
+
+ /* Register descriptions */
+ struct list_head reg_data_types;
+ struct list_head core_reg_descriptions;
+ struct list_head aux_reg_descriptions;
+ struct list_head bcr_reg_descriptions;
+ unsigned long num_regs;
+ unsigned long num_core_regs;
+ unsigned long num_aux_regs;
+ unsigned long num_bcr_regs;
+ unsigned long last_general_reg;
+
+ /* PC register location in register cache. */
+ unsigned long pc_index_in_cache;
+ /* DEBUG register location in register cache. */
+ unsigned long debug_index_in_cache;
+};
+
+/* Borrowed from nds32.h */
+#define CHECK_RETVAL(action) \
+ do { \
+ int __retval = (action); \
+ if (__retval != ERROR_OK) { \
+ LOG_DEBUG("error while calling \"%s\"", \
+ # action); \
+ return __retval; \
+ } \
+ } while (0)
+
+#define JIM_CHECK_RETVAL(action) \
+ do { \
+ int __retval = (action); \
+ if (__retval != JIM_OK) { \
+ LOG_DEBUG("error while calling \"%s\"", \
+ # action); \
+ return __retval; \
+ } \
+ } while (0)
+
+static inline struct arc_common *target_to_arc(struct target *target)
+{
+ return target->arch_info;
+}
+
+
+/* ARC Register description */
+struct arc_reg_desc {
+
+ struct target *target;
+
+ /* Register name */
+ char *name;
+
+ /* Actual place of storing reg_value */
+ uint8_t reg_value[4];
+
+ /* Actual place of storing register feature */
+ struct reg_feature feature;
+
+ /* GDB XML feature */
+ char *gdb_xml_feature;
+
+ /* Is this a register in g/G-packet? */
+ bool is_general;
+
+ /* Architectural number: core reg num or AUX reg num */
+ uint32_t arch_num;
+
+ /* Core or AUX register? */
+ bool is_core;
+
+ /* Build configuration register? */
+ bool is_bcr;
+
+ /* Data type */
+ struct reg_data_type *data_type;
+
+ struct list_head list;
+};
+
+/* Error codes */
+#define ERROR_ARC_REGISTER_NOT_FOUND (-700)
+#define ERROR_ARC_REGISTER_FIELD_NOT_FOUND (-701)
+#define ERROR_ARC_REGISTER_IS_NOT_STRUCT (-702)
+#define ERROR_ARC_FIELD_IS_NOT_BITFIELD (-703)
+#define ERROR_ARC_REGTYPE_NOT_FOUND (-704)
+
+void free_reg_desc(struct arc_reg_desc *r);
+
+
+void arc_reg_data_type_add(struct target *target,
+ struct arc_reg_data_type *data_type);
+
+int arc_reg_add(struct target *target, struct arc_reg_desc *arc_reg,
+ const char * const type_name, const size_t type_name_len);
+
+struct reg *arc_reg_get_by_name(struct reg_cache *first,
+ const char *name, bool search_all);
+
+int arc_reg_get_field(struct target *target, const char *reg_name,
+ const char *field_name, uint32_t *value_ptr);
+
+#endif /* OPENOCD_TARGET_ARC_H */
diff --git a/src/target/arc_cmd.c b/src/target/arc_cmd.c
new file mode 100644
index 0000000..3f6caf7
--- /dev/null
+++ b/src/target/arc_cmd.c
@@ -0,0 +1,977 @@
+/***************************************************************************
+ * Copyright (C) 2013-2015,2019-2020 Synopsys, Inc. *
+ * Frank Dols <frank.dols@synopsys.com> *
+ * Mischa Jonker <mischa.jonker@synopsys.com> *
+ * Anton Kolesov <anton.kolesov@synopsys.com> *
+ * Evgeniy Didin <didin@synopsys.com> *
+ * *
+ * SPDX-License-Identifier: GPL-2.0-or-later *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "arc.h"
+
+/* --------------------------------------------------------------------------
+ *
+ * ARC targets expose command interface.
+ * It can be accessed via GDB through the (gdb) monitor command.
+ *
+ * ------------------------------------------------------------------------- */
+
+
+static int arc_cmd_jim_get_uint32(Jim_GetOptInfo *goi, uint32_t *value)
+{
+ jim_wide value_wide;
+ JIM_CHECK_RETVAL(Jim_GetOpt_Wide(goi, &value_wide));
+ *value = (uint32_t)value_wide;
+ return JIM_OK;
+}
+
+enum add_reg_types {
+ CFG_ADD_REG_TYPE_FLAG,
+ CFG_ADD_REG_TYPE_STRUCT,
+};
+/* Add flags register data type */
+enum add_reg_type_flags {
+ CFG_ADD_REG_TYPE_FLAGS_NAME,
+ CFG_ADD_REG_TYPE_FLAGS_FLAG,
+};
+
+static Jim_Nvp nvp_add_reg_type_flags_opts[] = {
+ { .name = "-name", .value = CFG_ADD_REG_TYPE_FLAGS_NAME },
+ { .name = "-flag", .value = CFG_ADD_REG_TYPE_FLAGS_FLAG },
+ { .name = NULL, .value = -1 }
+};
+
+/* Helper function to check if all field required for register
+ * are set up */
+static const char *validate_register(const struct arc_reg_desc * const reg, bool arch_num_set)
+{
+ /* Check that required fields are set */
+ if (!reg->name)
+ return "-name option is required";
+ if (!reg->gdb_xml_feature)
+ return "-feature option is required";
+ if (!arch_num_set)
+ return "-num option is required";
+ if (reg->is_bcr && reg->is_core)
+ return "Register cannot be both -core and -bcr.";
+ return NULL;
+}
+
+/* Helper function to read the name of register type or register from
+ * configure files */
+static int jim_arc_read_reg_name_field(Jim_GetOptInfo *goi,
+ const char **name, int *name_len)
+{
+ int e = JIM_OK;
+
+ if (!goi->argc) {
+ Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-name <name> ...");
+ return JIM_ERR;
+ }
+ e = Jim_GetOpt_String(goi, name, name_len);
+ return e;
+}
+
+/* Helper function to read bitfields/flags of register type. */
+static int jim_arc_read_reg_type_field(Jim_GetOptInfo *goi, const char **field_name, int *field_name_len,
+ struct arc_reg_bitfield *bitfields, int cur_field, int type)
+{
+ jim_wide start_pos, end_pos;
+
+ int e = JIM_OK;
+ if ((type == CFG_ADD_REG_TYPE_STRUCT && goi->argc < 3) ||
+ (type == CFG_ADD_REG_TYPE_FLAG && goi->argc < 2)) {
+ Jim_SetResultFormatted(goi->interp, "Not enough argmunets after -flag/-bitfield");
+ return JIM_ERR;
+ }
+
+ e = Jim_GetOpt_String(goi, field_name, field_name_len);
+ if (e != JIM_OK)
+ return e;
+
+ /* read start position of bitfield/flag */
+ e = Jim_GetOpt_Wide(goi, &start_pos);
+ if (e != JIM_OK)
+ return e;
+
+ end_pos = start_pos;
+
+ /* Check if any argnuments remain,
+ * set bitfields[cur_field].end if flag is multibit */
+ if (goi->argc > 0)
+ /* Check current argv[0], if it is equal to "-flag",
+ * than bitfields[cur_field].end remains start */
+ if ((strcmp(Jim_String(goi->argv[0]), "-flag") && type == CFG_ADD_REG_TYPE_FLAG)
+ || (type == CFG_ADD_REG_TYPE_STRUCT)) {
+ e = Jim_GetOpt_Wide(goi, &end_pos);
+ if (e != JIM_OK) {
+ Jim_SetResultFormatted(goi->interp, "Error reading end position");
+ return e;
+ }
+ }
+
+ bitfields[cur_field].bitfield.start = start_pos;
+ bitfields[cur_field].bitfield.end = end_pos;
+ if ((end_pos != start_pos) || (type == CFG_ADD_REG_TYPE_STRUCT))
+ bitfields[cur_field].bitfield.type = REG_TYPE_INT;
+ return e;
+}
+
+static int jim_arc_add_reg_type_flags(Jim_Interp *interp, int argc,
+ Jim_Obj * const *argv)
+{
+ Jim_GetOptInfo goi;
+ JIM_CHECK_RETVAL(Jim_GetOpt_Setup(&goi, interp, argc-1, argv+1));
+
+ LOG_DEBUG("-");
+
+ struct command_context *ctx;
+ struct target *target;
+
+ ctx = current_command_context(interp);
+ assert(ctx);
+ target = get_current_target(ctx);
+ if (!target) {
+ Jim_SetResultFormatted(goi.interp, "No current target");
+ return JIM_ERR;
+ }
+
+ int e = JIM_OK;
+
+ /* Check if the amount of argnuments is not zero */
+ if (goi.argc <= 0) {
+ Jim_SetResultFormatted(goi.interp, "The command has no argnuments");
+ return JIM_ERR;
+ }
+
+ /* Estimate number of registers as (argc - 2)/3 as each -flag option has 2
+ * arguments while -name is required. */
+ unsigned int fields_sz = (goi.argc - 2) / 3;
+ unsigned int cur_field = 0;
+
+ /* Tha maximum amount of bitfilds is 32 */
+ if (fields_sz > 32) {
+ Jim_SetResultFormatted(goi.interp, "The amount of bitfields exceed 32");
+ return JIM_ERR;
+ }
+
+ struct arc_reg_data_type *type = calloc(1, sizeof(*type));
+ struct reg_data_type_flags *flags = &type->data_type_flags;
+ struct reg_data_type_flags_field *fields = calloc(fields_sz, sizeof(*fields));
+ struct arc_reg_bitfield *bitfields = calloc(fields_sz, sizeof(*type));
+ if (!(type && fields && bitfields)) {
+ Jim_SetResultFormatted(goi.interp, "Failed to allocate memory.");
+ goto fail;
+ }
+
+ /* Initialize type */
+ type->bitfields = bitfields;
+ type->data_type.id = type->data_type_id;
+ type->data_type.type = REG_TYPE_ARCH_DEFINED;
+ type->data_type.type_class = REG_TYPE_CLASS_FLAGS;
+ type->data_type.reg_type_flags = flags;
+ flags->size = 4; /* For now ARC has only 32-bit registers */
+
+ while (goi.argc > 0 && e == JIM_OK) {
+ Jim_Nvp *n;
+ e = Jim_GetOpt_Nvp(&goi, nvp_add_reg_type_flags_opts, &n);
+ if (e != JIM_OK) {
+ Jim_GetOpt_NvpUnknown(&goi, nvp_add_reg_type_flags_opts, 0);
+ continue;
+ }
+
+ switch (n->value) {
+ case CFG_ADD_REG_TYPE_FLAGS_NAME:
+ {
+ const char *name = NULL;
+ int name_len = 0;
+
+ e = jim_arc_read_reg_name_field(&goi, &name, &name_len);
+ if (e != JIM_OK) {
+ Jim_SetResultFormatted(goi.interp, "Unable to read reg name.");
+ goto fail;
+ }
+
+ if (name_len > REG_TYPE_MAX_NAME_LENGTH) {
+ Jim_SetResultFormatted(goi.interp, "Reg type name is too big.");
+ goto fail;
+ }
+
+ strncpy((void *)type->data_type.id, name, name_len);
+ if (!type->data_type.id) {
+ Jim_SetResultFormatted(goi.interp, "Unable to setup reg type name.");
+ goto fail;
+ }
+
+ break;
+ }
+
+ case CFG_ADD_REG_TYPE_FLAGS_FLAG:
+ {
+ const char *field_name = NULL;
+ int field_name_len = 0;
+
+ e = jim_arc_read_reg_type_field(&goi, &field_name, &field_name_len, bitfields,
+ cur_field, CFG_ADD_REG_TYPE_FLAG);
+ if (e != JIM_OK) {
+ Jim_SetResultFormatted(goi.interp, "Unable to add reg_type_flag field.");
+ goto fail;
+ }
+
+ if (field_name_len > REG_TYPE_MAX_NAME_LENGTH) {
+ Jim_SetResultFormatted(goi.interp, "Reg type field_name_len is too big.");
+ goto fail;
+ }
+
+ fields[cur_field].name = bitfields[cur_field].name;
+ strncpy(bitfields[cur_field].name, field_name, field_name_len);
+ if (!fields[cur_field].name) {
+ Jim_SetResultFormatted(goi.interp, "Unable to setup field name. ");
+ goto fail;
+ }
+
+ fields[cur_field].bitfield = &(bitfields[cur_field].bitfield);
+ if (cur_field > 0)
+ fields[cur_field - 1].next = &(fields[cur_field]);
+ else
+ flags->fields = fields;
+
+ cur_field += 1;
+ break;
+ }
+ }
+ }
+
+ if (!type->data_type.id) {
+ Jim_SetResultFormatted(goi.interp, "-name is a required option");
+ goto fail;
+ }
+
+ arc_reg_data_type_add(target, type);
+
+ LOG_DEBUG("added flags type {name=%s}", type->data_type.id);
+
+ return JIM_OK;
+fail:
+ free(type);
+ free(fields);
+ free(bitfields);
+
+ return JIM_ERR;
+}
+
+/* Add struct register data type */
+enum add_reg_type_struct {
+ CFG_ADD_REG_TYPE_STRUCT_NAME,
+ CFG_ADD_REG_TYPE_STRUCT_BITFIELD,
+};
+
+static Jim_Nvp nvp_add_reg_type_struct_opts[] = {
+ { .name = "-name", .value = CFG_ADD_REG_TYPE_STRUCT_NAME },
+ { .name = "-bitfield", .value = CFG_ADD_REG_TYPE_STRUCT_BITFIELD },
+ { .name = NULL, .value = -1 }
+};
+
+static int jim_arc_set_aux_reg(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
+{
+
+ struct command_context *context;
+ struct target *target;
+ uint32_t regnum;
+ uint32_t value;
+
+ Jim_GetOptInfo goi;
+ JIM_CHECK_RETVAL(Jim_GetOpt_Setup(&goi, interp, argc-1, argv+1));
+
+ if (goi.argc != 2) {
+ Jim_SetResultFormatted(goi.interp,
+ "usage: %s <aux_reg_num> <aux_reg_value>", Jim_GetString(argv[0], NULL));
+ return JIM_ERR;
+ }
+
+ context = current_command_context(interp);
+ assert(context);
+
+ target = get_current_target(context);
+ if (!target) {
+ Jim_SetResultFormatted(goi.interp, "No current target");
+ return JIM_ERR;
+ }
+
+ /* Register number */
+ JIM_CHECK_RETVAL(arc_cmd_jim_get_uint32(&goi, &regnum));
+
+ /* Register value */
+ JIM_CHECK_RETVAL(arc_cmd_jim_get_uint32(&goi, &value));
+
+ struct arc_common *arc = target_to_arc(target);
+ assert(arc);
+
+ CHECK_RETVAL(arc_jtag_write_aux_reg_one(&arc->jtag_info, regnum, value));
+
+ return ERROR_OK;
+}
+
+static int jim_arc_get_aux_reg(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
+{
+ struct command_context *context;
+ struct target *target;
+ uint32_t regnum;
+ uint32_t value;
+
+ Jim_GetOptInfo goi;
+ JIM_CHECK_RETVAL(Jim_GetOpt_Setup(&goi, interp, argc-1, argv+1));
+
+ if (goi.argc != 1) {
+ Jim_SetResultFormatted(goi.interp,
+ "usage: %s <aux_reg_num>", Jim_GetString(argv[0], NULL));
+ return JIM_ERR;
+ }
+
+ context = current_command_context(interp);
+ assert(context);
+
+ target = get_current_target(context);
+ if (!target) {
+ Jim_SetResultFormatted(goi.interp, "No current target");
+ return JIM_ERR;
+ }
+
+ /* Register number */
+ JIM_CHECK_RETVAL(arc_cmd_jim_get_uint32(&goi, &regnum));
+
+ struct arc_common *arc = target_to_arc(target);
+ assert(arc);
+
+ CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, regnum, &value));
+ Jim_SetResultInt(interp, value);
+
+ return ERROR_OK;
+}
+
+static int jim_arc_get_core_reg(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
+{
+ struct command_context *context;
+ struct target *target;
+ uint32_t regnum;
+ uint32_t value;
+
+ Jim_GetOptInfo goi;
+ JIM_CHECK_RETVAL(Jim_GetOpt_Setup(&goi, interp, argc-1, argv+1));
+
+ if (goi.argc != 1) {
+ Jim_SetResultFormatted(goi.interp,
+ "usage: %s <core_reg_num>", Jim_GetString(argv[0], NULL));
+ return JIM_ERR;
+ }
+
+ context = current_command_context(interp);
+ assert(context);
+
+ target = get_current_target(context);
+ if (!target) {
+ Jim_SetResultFormatted(goi.interp, "No current target");
+ return JIM_ERR;
+ }
+
+ /* Register number */
+ JIM_CHECK_RETVAL(arc_cmd_jim_get_uint32(&goi, &regnum));
+ if (regnum > CORE_REG_MAX_NUMBER || regnum == CORE_R61_NUM || regnum == CORE_R62_NUM) {
+ Jim_SetResultFormatted(goi.interp, "Core register number %i " \
+ "is invalid. Must less then 64 and not 61 and 62.", regnum);
+ return JIM_ERR;
+ }
+
+ struct arc_common *arc = target_to_arc(target);
+ assert(arc);
+
+ /* Read value */
+ CHECK_RETVAL(arc_jtag_read_core_reg_one(&arc->jtag_info, regnum, &value));
+ Jim_SetResultInt(interp, value);
+
+ return ERROR_OK;
+}
+
+static int jim_arc_set_core_reg(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
+{
+ struct command_context *context;
+ struct target *target;
+ uint32_t regnum;
+ uint32_t value;
+
+ Jim_GetOptInfo goi;
+ JIM_CHECK_RETVAL(Jim_GetOpt_Setup(&goi, interp, argc-1, argv+1));
+
+ if (goi.argc != 2) {
+ Jim_SetResultFormatted(goi.interp,
+ "usage: %s <core_reg_num> <core_reg_value>", Jim_GetString(argv[0], NULL));
+ return JIM_ERR;
+ }
+
+ context = current_command_context(interp);
+ assert(context);
+
+ target = get_current_target(context);
+ if (!target) {
+ Jim_SetResultFormatted(goi.interp, "No current target");
+ return JIM_ERR;
+ }
+
+ /* Register number */
+ JIM_CHECK_RETVAL(arc_cmd_jim_get_uint32(&goi, &regnum));
+ if (regnum > CORE_REG_MAX_NUMBER || regnum == CORE_R61_NUM || regnum == CORE_R62_NUM) {
+ Jim_SetResultFormatted(goi.interp, "Core register number %i " \
+ "is invalid. Must less then 64 and not 61 and 62.", regnum);
+ return JIM_ERR;
+ }
+
+ /* Register value */
+ JIM_CHECK_RETVAL(arc_cmd_jim_get_uint32(&goi, &value));
+
+ struct arc_common *arc = target_to_arc(target);
+ assert(arc);
+
+ CHECK_RETVAL(arc_jtag_write_core_reg_one(&arc->jtag_info, regnum, value));
+
+ return ERROR_OK;
+}
+
+static const struct command_registration arc_jtag_command_group[] = {
+ {
+ .name = "get-aux-reg",
+ .jim_handler = jim_arc_get_aux_reg,
+ .mode = COMMAND_EXEC,
+ .help = "Get AUX register by number. This command does a " \
+ "raw JTAG request that bypasses OpenOCD register cache "\
+ "and thus is unsafe and can have unexpected consequences. "\
+ "Use at your own risk.",
+ .usage = "arc jtag get-aux-reg <regnum>"
+ },
+ {
+ .name = "set-aux-reg",
+ .jim_handler = jim_arc_set_aux_reg,
+ .mode = COMMAND_EXEC,
+ .help = "Set AUX register by number. This command does a " \
+ "raw JTAG request that bypasses OpenOCD register cache "\
+ "and thus is unsafe and can have unexpected consequences. "\
+ "Use at your own risk.",
+ .usage = "arc jtag set-aux-reg <regnum> <value>"
+ },
+ {
+ .name = "get-core-reg",
+ .jim_handler = jim_arc_get_core_reg,
+ .mode = COMMAND_EXEC,
+ .help = "Get/Set core register by number. This command does a " \
+ "raw JTAG request that bypasses OpenOCD register cache "\
+ "and thus is unsafe and can have unexpected consequences. "\
+ "Use at your own risk.",
+ .usage = "arc jtag get-core-reg <regnum> [<value>]"
+ },
+ {
+ .name = "set-core-reg",
+ .jim_handler = jim_arc_set_core_reg,
+ .mode = COMMAND_EXEC,
+ .help = "Get/Set core register by number. This command does a " \
+ "raw JTAG request that bypasses OpenOCD register cache "\
+ "and thus is unsafe and can have unexpected consequences. "\
+ "Use at your own risk.",
+ .usage = "arc jtag set-core-reg <regnum> [<value>]"
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+
+/* This function supports only bitfields. */
+static int jim_arc_add_reg_type_struct(Jim_Interp *interp, int argc,
+ Jim_Obj * const *argv)
+{
+ Jim_GetOptInfo goi;
+ JIM_CHECK_RETVAL(Jim_GetOpt_Setup(&goi, interp, argc-1, argv+1));
+
+ LOG_DEBUG("-");
+
+ struct command_context *ctx;
+ struct target *target;
+
+ ctx = current_command_context(interp);
+ assert(ctx);
+ target = get_current_target(ctx);
+ if (!target) {
+ Jim_SetResultFormatted(goi.interp, "No current target");
+ return JIM_ERR;
+ }
+
+ int e = JIM_OK;
+
+ /* Check if the amount of argnuments is not zero */
+ if (goi.argc <= 0) {
+ Jim_SetResultFormatted(goi.interp, "The command has no argnuments");
+ return JIM_ERR;
+ }
+
+ /* Estimate number of registers as (argc - 2)/4 as each -bitfield option has 3
+ * arguments while -name is required. */
+ unsigned int fields_sz = (goi.argc - 2) / 4;
+ unsigned int cur_field = 0;
+
+ /* Tha maximum amount of bitfilds is 32 */
+ if (fields_sz > 32) {
+ Jim_SetResultFormatted(goi.interp, "The amount of bitfields exceed 32");
+ return JIM_ERR;
+ }
+
+ struct arc_reg_data_type *type = calloc(1, sizeof(*type));
+ struct reg_data_type_struct *struct_type = &type->data_type_struct;
+ struct reg_data_type_struct_field *fields = calloc(fields_sz, sizeof(*fields));
+ struct arc_reg_bitfield *bitfields = calloc(fields_sz, sizeof(*type));
+ if (!(type && fields && bitfields)) {
+ Jim_SetResultFormatted(goi.interp, "Failed to allocate memory.");
+ goto fail;
+ }
+
+ /* Initialize type */
+ type->data_type.id = type->data_type_id;
+ type->bitfields = bitfields;
+ type->data_type.type = REG_TYPE_ARCH_DEFINED;
+ type->data_type.type_class = REG_TYPE_CLASS_STRUCT;
+ type->data_type.reg_type_struct = struct_type;
+ struct_type->size = 4; /* For now ARC has only 32-bit registers */
+
+ while (goi.argc > 0 && e == JIM_OK) {
+ Jim_Nvp *n;
+ e = Jim_GetOpt_Nvp(&goi, nvp_add_reg_type_struct_opts, &n);
+ if (e != JIM_OK) {
+ Jim_GetOpt_NvpUnknown(&goi, nvp_add_reg_type_struct_opts, 0);
+ continue;
+ }
+
+ switch (n->value) {
+ case CFG_ADD_REG_TYPE_STRUCT_NAME:
+ {
+ const char *name = NULL;
+ int name_len = 0;
+
+ e = jim_arc_read_reg_name_field(&goi, &name, &name_len);
+ if (e != JIM_OK) {
+ Jim_SetResultFormatted(goi.interp, "Unable to read reg name.");
+ goto fail;
+ }
+
+ if (name_len > REG_TYPE_MAX_NAME_LENGTH) {
+ Jim_SetResultFormatted(goi.interp, "Reg type name is too big.");
+ goto fail;
+ }
+
+ strncpy((void *)type->data_type.id, name, name_len);
+ if (!type->data_type.id) {
+ Jim_SetResultFormatted(goi.interp, "Unable to setup reg type name.");
+ goto fail;
+ }
+
+ break;
+ }
+ case CFG_ADD_REG_TYPE_STRUCT_BITFIELD:
+ {
+ const char *field_name = NULL;
+ int field_name_len = 0;
+ e = jim_arc_read_reg_type_field(&goi, &field_name, &field_name_len, bitfields,
+ cur_field, CFG_ADD_REG_TYPE_STRUCT);
+ if (e != JIM_OK) {
+ Jim_SetResultFormatted(goi.interp, "Unable to add reg_type_struct field.");
+ goto fail;
+ }
+
+ if (field_name_len > REG_TYPE_MAX_NAME_LENGTH) {
+ Jim_SetResultFormatted(goi.interp, "Reg type field_name_len is too big.");
+ goto fail;
+ }
+
+ fields[cur_field].name = bitfields[cur_field].name;
+ strncpy(bitfields[cur_field].name, field_name, field_name_len);
+ if (!fields[cur_field].name) {
+ Jim_SetResultFormatted(goi.interp, "Unable to setup field name. ");
+ goto fail;
+ }
+
+ fields[cur_field].bitfield = &(bitfields[cur_field].bitfield);
+ fields[cur_field].use_bitfields = true;
+ if (cur_field > 0)
+ fields[cur_field - 1].next = &(fields[cur_field]);
+ else
+ struct_type->fields = fields;
+
+ cur_field += 1;
+
+ break;
+ }
+ }
+ }
+
+ if (!type->data_type.id) {
+ Jim_SetResultFormatted(goi.interp, "-name is a required option");
+ goto fail;
+ }
+
+ arc_reg_data_type_add(target, type);
+ LOG_DEBUG("added struct type {name=%s}", type->data_type.id);
+ return JIM_OK;
+
+fail:
+ free(type);
+ free(fields);
+ free(bitfields);
+
+ return JIM_ERR;
+}
+
+/* Add register */
+enum opts_add_reg {
+ CFG_ADD_REG_NAME,
+ CFG_ADD_REG_ARCH_NUM,
+ CFG_ADD_REG_IS_CORE,
+ CFG_ADD_REG_IS_BCR,
+ CFG_ADD_REG_GDB_FEATURE,
+ CFG_ADD_REG_TYPE,
+ CFG_ADD_REG_GENERAL,
+};
+
+static Jim_Nvp opts_nvp_add_reg[] = {
+ { .name = "-name", .value = CFG_ADD_REG_NAME },
+ { .name = "-num", .value = CFG_ADD_REG_ARCH_NUM },
+ { .name = "-core", .value = CFG_ADD_REG_IS_CORE },
+ { .name = "-bcr", .value = CFG_ADD_REG_IS_BCR },
+ { .name = "-feature", .value = CFG_ADD_REG_GDB_FEATURE },
+ { .name = "-type", .value = CFG_ADD_REG_TYPE },
+ { .name = "-g", .value = CFG_ADD_REG_GENERAL },
+ { .name = NULL, .value = -1 }
+};
+
+void free_reg_desc(struct arc_reg_desc *r)
+{
+ free(r->name);
+ free(r->gdb_xml_feature);
+ free(r);
+}
+
+static int jim_arc_add_reg(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
+{
+ Jim_GetOptInfo goi;
+ JIM_CHECK_RETVAL(Jim_GetOpt_Setup(&goi, interp, argc-1, argv+1));
+
+ struct arc_reg_desc *reg = calloc(1, sizeof(*reg));
+ if (!reg) {
+ Jim_SetResultFormatted(goi.interp, "Failed to allocate memory.");
+ return JIM_ERR;
+ }
+
+ /* There is no architecture number that we could treat as invalid, so
+ * separate variable requried to ensure that arch num has been set. */
+ bool arch_num_set = false;
+ const char *type_name = "int"; /* Default type */
+ int type_name_len = strlen(type_name);
+ int e = ERROR_OK;
+
+ /* At least we need to specify 4 parameters: name, number, type and gdb_feature,
+ * which means there should be 8 arguments */
+ if (goi.argc < 8) {
+ free_reg_desc(reg);
+ Jim_SetResultFormatted(goi.interp,
+ "Should be at least 8 argnuments: -name <name> "
+ "-num <num> -type <type> -feature <gdb_feature>.");
+ return JIM_ERR;
+ }
+
+ /* Parse options. */
+ while (goi.argc > 0) {
+ Jim_Nvp *n;
+ e = Jim_GetOpt_Nvp(&goi, opts_nvp_add_reg, &n);
+ if (e != JIM_OK) {
+ Jim_GetOpt_NvpUnknown(&goi, opts_nvp_add_reg, 0);
+ free_reg_desc(reg);
+ return e;
+ }
+
+ switch (n->value) {
+ case CFG_ADD_REG_NAME:
+ {
+ const char *reg_name = NULL;
+ int reg_name_len = 0;
+
+ e = jim_arc_read_reg_name_field(&goi, &reg_name, &reg_name_len);
+ if (e != JIM_OK) {
+ Jim_SetResultFormatted(goi.interp, "Unable to read register name.");
+ free_reg_desc(reg);
+ return e;
+ }
+
+ reg->name = strndup(reg_name, reg_name_len);
+ break;
+ }
+ case CFG_ADD_REG_IS_CORE:
+ reg->is_core = true;
+ break;
+ case CFG_ADD_REG_IS_BCR:
+ reg->is_bcr = true;
+ break;
+ case CFG_ADD_REG_ARCH_NUM:
+ {
+ jim_wide archnum;
+
+ if (!goi.argc) {
+ free_reg_desc(reg);
+ Jim_WrongNumArgs(interp, goi.argc, goi.argv, "-num <int> ...");
+ return JIM_ERR;
+ }
+
+ e = Jim_GetOpt_Wide(&goi, &archnum);
+ if (e != JIM_OK) {
+ free_reg_desc(reg);
+ return e;
+ }
+
+ reg->arch_num = archnum;
+ arch_num_set = true;
+ break;
+ }
+ case CFG_ADD_REG_GDB_FEATURE:
+ {
+ const char *feature = NULL;
+ int feature_len = 0;
+
+ e = jim_arc_read_reg_name_field(&goi, &feature, &feature_len);
+ if (e != JIM_OK) {
+ Jim_SetResultFormatted(goi.interp, "Unable to read gdb_feature.");
+ free_reg_desc(reg);
+ return e;
+ }
+
+ reg->gdb_xml_feature = strndup(feature, feature_len);
+ break;
+ }
+ case CFG_ADD_REG_TYPE:
+ e = jim_arc_read_reg_name_field(&goi, &type_name, &type_name_len);
+ if (e != JIM_OK) {
+ Jim_SetResultFormatted(goi.interp, "Unable to read register type.");
+ free_reg_desc(reg);
+ return e;
+ }
+
+ break;
+ case CFG_ADD_REG_GENERAL:
+ reg->is_general = true;
+ break;
+ default:
+ LOG_DEBUG("Error: Unknown parameter");
+ free_reg_desc(reg);
+ return JIM_ERR;
+ }
+ }
+
+ /* Check that required fields are set */
+ const char * const errmsg = validate_register(reg, arch_num_set);
+ if (errmsg) {
+ Jim_SetResultFormatted(goi.interp, errmsg);
+ free_reg_desc(reg);
+ return JIM_ERR;
+ }
+
+ /* Add new register */
+ struct command_context *ctx;
+ struct target *target;
+
+ ctx = current_command_context(interp);
+ assert(ctx);
+ target = get_current_target(ctx);
+ if (!target) {
+ Jim_SetResultFormatted(goi.interp, "No current target");
+ return JIM_ERR;
+ }
+
+ reg->target = target;
+
+ e = arc_reg_add(target, reg, type_name, type_name_len);
+ if (e == ERROR_ARC_REGTYPE_NOT_FOUND) {
+ Jim_SetResultFormatted(goi.interp,
+ "Cannot find type `%s' for register `%s'.",
+ type_name, reg->name);
+ free_reg_desc(reg);
+ return JIM_ERR;
+ }
+
+ return e;
+}
+
+/* arc set-reg-exists ($reg_name)+
+ * Accepts any amount of register names - will set them as existing in a loop.*/
+COMMAND_HANDLER(arc_set_reg_exists)
+{
+ struct target * const target = get_current_target(CMD_CTX);
+ if (!target) {
+ command_print(CMD, "Unable to get current target.");
+ return JIM_ERR;
+ }
+
+ if (!CMD_ARGC) {
+ command_print(CMD, "At least one register name must be specified.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ for (unsigned int i = 0; i < CMD_ARGC; i++) {
+ const char * const reg_name = CMD_ARGV[i];
+ struct reg * const r = arc_reg_get_by_name(target->reg_cache, reg_name, true);
+
+ if (!r) {
+ command_print(CMD, "Register `%s' is not found.", reg_name);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+
+ r->exist = true;
+ }
+
+ return JIM_OK;
+}
+
+/* arc reg-field ($reg_name) ($reg_field)
+ * Reads struct type register field */
+static int jim_arc_get_reg_field(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
+{
+ Jim_GetOptInfo goi;
+ const char *reg_name, *field_name;
+ uint32_t value;
+ int retval;
+
+ JIM_CHECK_RETVAL(Jim_GetOpt_Setup(&goi, interp, argc-1, argv+1));
+
+ LOG_DEBUG("Reading register field");
+ if (goi.argc != 2) {
+ if (!goi.argc)
+ Jim_WrongNumArgs(interp, goi.argc, goi.argv, "<regname> <fieldname>");
+ else if (goi.argc == 1)
+ Jim_WrongNumArgs(interp, goi.argc, goi.argv, "<fieldname>");
+ else
+ Jim_WrongNumArgs(interp, goi.argc, goi.argv, "<regname> <fieldname>");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ JIM_CHECK_RETVAL(Jim_GetOpt_String(&goi, &reg_name, NULL));
+ JIM_CHECK_RETVAL(Jim_GetOpt_String(&goi, &field_name, NULL));
+ assert(reg_name);
+ assert(field_name);
+
+ struct command_context * const ctx = current_command_context(interp);
+ assert(ctx);
+ struct target * const target = get_current_target(ctx);
+ if (!target) {
+ Jim_SetResultFormatted(goi.interp, "No current target");
+ return JIM_ERR;
+ }
+
+ retval = arc_reg_get_field(target, reg_name, field_name, &value);
+
+ switch (retval) {
+ case ERROR_OK:
+ break;
+ case ERROR_ARC_REGISTER_NOT_FOUND:
+ Jim_SetResultFormatted(goi.interp,
+ "Register `%s' has not been found.", reg_name);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ case ERROR_ARC_REGISTER_IS_NOT_STRUCT:
+ Jim_SetResultFormatted(goi.interp,
+ "Register `%s' must have 'struct' type.", reg_name);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ case ERROR_ARC_REGISTER_FIELD_NOT_FOUND:
+ Jim_SetResultFormatted(goi.interp,
+ "Field `%s' has not been found in register `%s'.",
+ field_name, reg_name);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ case ERROR_ARC_FIELD_IS_NOT_BITFIELD:
+ Jim_SetResultFormatted(goi.interp,
+ "Field `%s' is not a 'bitfield' field in a structure.",
+ field_name);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ default:
+ /* Pass through other errors. */
+ return retval;
+ }
+
+ Jim_SetResultInt(interp, value);
+
+ return JIM_OK;
+}
+
+/* ----- Exported target commands ------------------------------------------ */
+
+static const struct command_registration arc_core_command_handlers[] = {
+{
+ .name = "add-reg-type-flags",
+ .jim_handler = jim_arc_add_reg_type_flags,
+ .mode = COMMAND_CONFIG,
+ .usage = "arc ardd-reg-type-flags -name <string> -flag <name> <position> "
+ "[-flag <name> <position>]...",
+ .help = "Add new 'flags' register data type. Only single bit flags "
+ "are supported. Type name is global. Bitsize of register is fixed "
+ "at 32 bits.",
+ },
+ {
+ .name = "add-reg-type-struct",
+ .jim_handler = jim_arc_add_reg_type_struct,
+ .mode = COMMAND_CONFIG,
+ .usage = "arc add-reg-type-struct -name <string> -bitfield <name> <start> <end> "
+ "[-bitfield <name> <start> <end>]...",
+ .help = "Add new 'struct' register data type. Only bit-fields are "
+ "supported so far, which means that for each bitfield start and end "
+ "position bits must be specified. GDB also support type-fields, "
+ "where common type can be used instead. Type name is global. Bitsize of "
+ "register is fixed at 32 bits.",
+ },
+ {
+ .name = "add-reg",
+ .jim_handler = jim_arc_add_reg,
+ .mode = COMMAND_CONFIG,
+ .usage = "arc add-reg -name <string> -num <int> -feature <string> [-gdbnum <int>] "
+ "[-core|-bcr] [-type <type_name>] [-g]",
+ .help = "Add new register. Name, architectural number and feature name "
+ "are requried options. GDB regnum will default to previous register "
+ "(gdbnum + 1) and shouldn't be specified in most cases. Type "
+ "defaults to default GDB 'int'.",
+ },
+ {
+ .name = "set-reg-exists",
+ .handler = arc_set_reg_exists,
+ .mode = COMMAND_ANY,
+ .usage = "arc set-reg-exists <register-name> [<register-name>]...",
+ .help = "Set that register exists. Accepts multiple register names as "
+ "arguments.",
+ },
+ {
+ .name = "get-reg-field",
+ .jim_handler = jim_arc_get_reg_field,
+ .mode = COMMAND_ANY,
+ .usage = "arc get-reg-field <regname> <field_name>",
+ .help = "Returns value of field in a register with 'struct' type.",
+ },
+ {
+ .name = "jtag",
+ .mode = COMMAND_ANY,
+ .help = "ARC JTAG specific commands",
+ .usage = "",
+ .chain = arc_jtag_command_group,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+const struct command_registration arc_monitor_command_handlers[] = {
+ {
+ .name = "arc",
+ .mode = COMMAND_ANY,
+ .help = "ARC monitor command group",
+ .usage = "Help info ...",
+ .chain = arc_core_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
diff --git a/src/target/arc_cmd.h b/src/target/arc_cmd.h
new file mode 100644
index 0000000..b2264eb
--- /dev/null
+++ b/src/target/arc_cmd.h
@@ -0,0 +1,16 @@
+/***************************************************************************
+ * Copyright (C) 2013-2014,2019-2020 Synopsys, Inc. *
+ * Frank Dols <frank.dols@synopsys.com> *
+ * Mischa Jonker <mischa.jonker@synopsys.com> *
+ * Anton Kolesov <anton.kolesov@synopsys.com> *
+ * Evgeniy Didin <didin@synopsys.com> *
+ * *
+ * SPDX-License-Identifier: GPL-2.0-or-later *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ARC_CMD_H
+#define OPENOCD_TARGET_ARC_CMD_H
+
+extern const struct command_registration arc_monitor_command_handlers[];
+
+#endif /* OPENOCD_TARGET_ARC_CMD_H */
diff --git a/src/target/arc_jtag.c b/src/target/arc_jtag.c
new file mode 100644
index 0000000..dd800a4
--- /dev/null
+++ b/src/target/arc_jtag.c
@@ -0,0 +1,542 @@
+/***************************************************************************
+ * Copyright (C) 2013-2014,2019-2020 Synopsys, Inc. *
+ * Frank Dols <frank.dols@synopsys.com> *
+ * Mischa Jonker <mischa.jonker@synopsys.com> *
+ * Anton Kolesov <anton.kolesov@synopsys.com> *
+ * Evgeniy Didin <didin@synopsys.com> *
+ * *
+ * SPDX-License-Identifier: GPL-2.0-or-later *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "arc.h"
+
+/*
+ * This functions sets instruction register in TAP. TAP end state is always
+ * IRPAUSE.
+ *
+ * @param jtag_info
+ * @param new_instr Instruction to write to instruction register.
+ */
+static void arc_jtag_enque_write_ir(struct arc_jtag *jtag_info, uint32_t
+ new_instr)
+{
+ uint32_t current_instr;
+ struct jtag_tap *tap;
+ uint8_t instr_buffer[sizeof(uint32_t)];
+
+ assert(jtag_info);
+ assert(jtag_info->tap);
+
+ tap = jtag_info->tap;
+
+ /* Do not set instruction if it is the same as current. */
+ current_instr = buf_get_u32(tap->cur_instr, 0, tap->ir_length);
+ if (current_instr == new_instr)
+ return;
+
+ struct scan_field field = {
+ .num_bits = tap->ir_length,
+ .out_value = instr_buffer
+ };
+ buf_set_u32(instr_buffer, 0, field.num_bits, new_instr);
+
+ /* From code in src/jtag/drivers/driver.c it look like that fields are
+ * copied so it is OK that field in this function is allocated in stack and
+ * thus this memory will be repurposed before jtag_execute_queue() will be
+ * invoked. */
+ jtag_add_ir_scan(tap, &field, TAP_IRPAUSE);
+}
+
+/**
+ * Read 4-byte word from data register.
+ *
+ * Unlike arc_jtag_write_data, this function returns byte-buffer, caller must
+ * convert this data to required format himself. This is done, because it is
+ * impossible to convert data before jtag_execute_queue() is invoked, so it
+ * cannot be done inside this function, so it has to operate with
+ * byte-buffers. Write function on the other hand can "write-and-forget", data
+ * is converted to byte-buffer before jtag_execute_queue().
+ *
+ * @param jtag_info
+ * @param data Array of bytes to read into.
+ * @param end_state End state after reading.
+ */
+static void arc_jtag_enque_read_dr(struct arc_jtag *jtag_info, uint8_t *data,
+ tap_state_t end_state)
+{
+
+ assert(jtag_info);
+ assert(jtag_info->tap);
+
+ struct scan_field field = {
+ .num_bits = 32,
+ .in_value = data
+ };
+
+ jtag_add_dr_scan(jtag_info->tap, 1, &field, end_state);
+}
+
+/**
+ * Write 4-byte word to data register.
+ *
+ * @param jtag_info
+ * @param data 4-byte word to write into data register.
+ * @param end_state End state after writing.
+ */
+static void arc_jtag_enque_write_dr(struct arc_jtag *jtag_info, uint32_t data,
+ tap_state_t end_state)
+{
+ uint8_t out_value[sizeof(uint32_t)];
+
+ assert(jtag_info);
+ assert(jtag_info->tap);
+
+ buf_set_u32(out_value, 0, 32, data);
+
+ struct scan_field field = {
+ .num_bits = 32,
+ .out_value = out_value
+ };
+
+ jtag_add_dr_scan(jtag_info->tap, 1, &field, end_state);
+}
+
+
+/**
+ * Set transaction in command register. This function sets instruction register
+ * and then transaction register, there is no need to invoke write_ir before
+ * invoking this function.
+ *
+ * @param jtag_info
+ * @param new_trans Transaction to write to transaction command register.
+ * @param end_state End state after writing.
+ */
+static void arc_jtag_enque_set_transaction(struct arc_jtag *jtag_info,
+ uint32_t new_trans, tap_state_t end_state)
+{
+ uint8_t out_value[sizeof(uint32_t)];
+
+ assert(jtag_info);
+ assert(jtag_info->tap);
+
+ /* No need to do anything. */
+ if (jtag_info->cur_trans == new_trans)
+ return;
+
+ /* Set instruction. We used to call write_ir at upper levels, however
+ * write_ir-write_transaction were constantly in pair, so to avoid code
+ * duplication this function does it self. For this reasons it is "set"
+ * instead of "write". */
+ arc_jtag_enque_write_ir(jtag_info, ARC_TRANSACTION_CMD_REG);
+ buf_set_u32(out_value, 0, ARC_TRANSACTION_CMD_REG_LENGTH, new_trans);
+ struct scan_field field = {
+ .num_bits = ARC_TRANSACTION_CMD_REG_LENGTH,
+ .out_value = out_value
+ };
+
+ jtag_add_dr_scan(jtag_info->tap, 1, &field, end_state);
+ jtag_info->cur_trans = new_trans;
+}
+
+/**
+ * Run reset through transaction set. None of the previous
+ * settings/commands/etc. are used anymore (or no influence).
+ */
+static void arc_jtag_enque_reset_transaction(struct arc_jtag *jtag_info)
+{
+ arc_jtag_enque_set_transaction(jtag_info, ARC_JTAG_CMD_NOP, TAP_IDLE);
+}
+
+static void arc_jtag_enque_status_read(struct arc_jtag * const jtag_info,
+ uint8_t * const buffer)
+{
+ assert(jtag_info);
+ assert(jtag_info->tap);
+ assert(buffer);
+
+ /* first writin code(0x8) of jtag status register in IR */
+ arc_jtag_enque_write_ir(jtag_info, ARC_JTAG_STATUS_REG);
+ /* Now reading dr performs jtag status register read */
+ arc_jtag_enque_read_dr(jtag_info, buffer, TAP_IDLE);
+}
+
+/* ----- Exported JTAG functions ------------------------------------------- */
+
+int arc_jtag_startup(struct arc_jtag *jtag_info)
+{
+ assert(jtag_info);
+
+ arc_jtag_enque_reset_transaction(jtag_info);
+
+ return jtag_execute_queue();
+}
+
+/** Read STATUS register. */
+int arc_jtag_status(struct arc_jtag * const jtag_info, uint32_t * const value)
+{
+ uint8_t buffer[sizeof(uint32_t)];
+
+ assert(jtag_info);
+ assert(jtag_info->tap);
+
+ /* Fill command queue. */
+ arc_jtag_enque_reset_transaction(jtag_info);
+ arc_jtag_enque_status_read(jtag_info, buffer);
+ arc_jtag_enque_reset_transaction(jtag_info);
+
+ /* Execute queue. */
+ CHECK_RETVAL(jtag_execute_queue());
+
+ /* Parse output. */
+ *value = buf_get_u32(buffer, 0, 32);
+
+ return ERROR_OK;
+}
+/* Helper function: Adding read/write register operation to queue */
+static void arc_jtag_enque_register_rw(struct arc_jtag *jtag_info, uint32_t *addr,
+ uint8_t *read_buffer, const uint32_t *write_buffer, uint32_t count)
+{
+ uint32_t i;
+
+ for (i = 0; i < count; i++) {
+ /* ARC jtag has optimization which is to increment ADDRESS_REG performing
+ * each transaction. Making sequential reads/writes we can set address for
+ * only first register in sequence, and than do read/write in cycle. */
+ if (i == 0 || (addr[i] != addr[i-1] + 1)) {
+ arc_jtag_enque_write_ir(jtag_info, ARC_JTAG_ADDRESS_REG);
+ /* Going to TAP_IDLE state we initiate jtag transaction.
+ * Reading data we must go to TAP_IDLE, because further
+ * the data would be read. In case of write we go to TAP_DRPAUSE,
+ * because we need to write data to Data register first. */
+ if (write_buffer)
+ arc_jtag_enque_write_dr(jtag_info, addr[i], TAP_DRPAUSE);
+ else
+ arc_jtag_enque_write_dr(jtag_info, addr[i], TAP_IDLE);
+ arc_jtag_enque_write_ir(jtag_info, ARC_JTAG_DATA_REG);
+ }
+ if (write_buffer)
+ arc_jtag_enque_write_dr(jtag_info, *(write_buffer + i), TAP_IDLE);
+ else
+ arc_jtag_enque_read_dr(jtag_info, read_buffer + i * 4, TAP_IDLE);
+ }
+ /* To prevent pollution of next regiter due to optimization it is necessary *
+ * to reset transaction */
+ arc_jtag_enque_reset_transaction(jtag_info);
+}
+
+/**
+ * Write registers. addr is an array of addresses, and those addresses can be
+ * in any order, though it is recommended that they are in sequential order
+ * where possible, as this reduces number of JTAG commands to transfer.
+ *
+ * @param jtag_info
+ * @param type Type of registers to write: core or aux.
+ * @param addr Array of registers numbers.
+ * @param count Amount of registers in arrays.
+ * @param values Array of register values.
+ */
+static int arc_jtag_write_registers(struct arc_jtag *jtag_info, uint32_t type,
+ uint32_t *addr, uint32_t count, const uint32_t *buffer)
+{
+ LOG_DEBUG("Writing to %s registers: addr[0]=0x%" PRIx32 ";count=%" PRIu32
+ ";buffer[0]=0x%08" PRIx32,
+ (type == ARC_JTAG_CORE_REG ? "core" : "aux"), *addr, count, *buffer);
+
+ if (!count) {
+ LOG_ERROR("Trying to write 0 registers");
+ return ERROR_FAIL;
+ }
+
+ arc_jtag_enque_reset_transaction(jtag_info);
+
+ /* What registers are we writing to? */
+ const uint32_t transaction = (type == ARC_JTAG_CORE_REG ?
+ ARC_JTAG_WRITE_TO_CORE_REG : ARC_JTAG_WRITE_TO_AUX_REG);
+ arc_jtag_enque_set_transaction(jtag_info, transaction, TAP_DRPAUSE);
+
+ arc_jtag_enque_register_rw(jtag_info, addr, NULL, buffer, count);
+
+ return jtag_execute_queue();
+}
+
+/**
+ * Read registers. addr is an array of addresses, and those addresses can be in
+ * any order, though it is recommended that they are in sequential order where
+ * possible, as this reduces number of JTAG commands to transfer.
+ *
+ * @param jtag_info
+ * @param type Type of registers to read: core or aux.
+ * @param addr Array of registers numbers.
+ * @param count Amount of registers in arrays.
+ * @param values Array of register values.
+ */
+static int arc_jtag_read_registers(struct arc_jtag *jtag_info, uint32_t type,
+ uint32_t *addr, uint32_t count, uint32_t *buffer)
+{
+ int retval;
+ uint32_t i;
+
+ assert(jtag_info);
+ assert(jtag_info->tap);
+
+ LOG_DEBUG("Reading %s registers: addr[0]=0x%" PRIx32 ";count=%" PRIu32,
+ (type == ARC_JTAG_CORE_REG ? "core" : "aux"), *addr, count);
+
+ if (!count) {
+ LOG_ERROR("Trying to read 0 registers");
+ return ERROR_FAIL;
+ }
+
+ arc_jtag_enque_reset_transaction(jtag_info);
+
+ /* What type of registers we are reading? */
+ const uint32_t transaction = (type == ARC_JTAG_CORE_REG ?
+ ARC_JTAG_READ_FROM_CORE_REG : ARC_JTAG_READ_FROM_AUX_REG);
+ arc_jtag_enque_set_transaction(jtag_info, transaction, TAP_DRPAUSE);
+
+ uint8_t *data_buf = calloc(sizeof(uint8_t), count * 4);
+
+ arc_jtag_enque_register_rw(jtag_info, addr, data_buf, NULL, count);
+
+ retval = jtag_execute_queue();
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Failed to execute jtag queue: %d", retval);
+ retval = ERROR_FAIL;
+ goto exit;
+ }
+
+ /* Convert byte-buffers to host /presentation. */
+ for (i = 0; i < count; i++)
+ buffer[i] = buf_get_u32(data_buf + 4 * i, 0, 32);
+
+ LOG_DEBUG("Read from register: buf[0]=0x%" PRIx32, buffer[0]);
+
+exit:
+ free(data_buf);
+
+ return retval;
+}
+
+
+/** Wrapper function to ease writing of one core register. */
+int arc_jtag_write_core_reg_one(struct arc_jtag *jtag_info, uint32_t addr,
+ uint32_t value)
+{
+ return arc_jtag_write_core_reg(jtag_info, &addr, 1, &value);
+}
+
+/**
+ * Write core registers. addr is an array of addresses, and those addresses can
+ * be in any order, though it is recommended that they are in sequential order
+ * where possible, as this reduces number of JTAG commands to transfer.
+ *
+ * @param jtag_info
+ * @param addr Array of registers numbers.
+ * @param count Amount of registers in arrays.
+ * @param values Array of register values.
+ */
+int arc_jtag_write_core_reg(struct arc_jtag *jtag_info, uint32_t *addr,
+ uint32_t count, const uint32_t *buffer)
+{
+ return arc_jtag_write_registers(jtag_info, ARC_JTAG_CORE_REG, addr, count,
+ buffer);
+}
+
+/** Wrapper function to ease reading of one core register. */
+int arc_jtag_read_core_reg_one(struct arc_jtag *jtag_info, uint32_t addr,
+ uint32_t *value)
+{
+ return arc_jtag_read_core_reg(jtag_info, &addr, 1, value);
+}
+
+/**
+ * Read core registers. addr is an array of addresses, and those addresses can
+ * be in any order, though it is recommended that they are in sequential order
+ * where possible, as this reduces number of JTAG commands to transfer.
+ *
+ * @param jtag_info
+ * @param addr Array of core register numbers.
+ * @param count Amount of registers in arrays.
+ * @param values Array of register values.
+ */
+int arc_jtag_read_core_reg(struct arc_jtag *jtag_info, uint32_t *addr,
+ uint32_t count, uint32_t *buffer)
+{
+ return arc_jtag_read_registers(jtag_info, ARC_JTAG_CORE_REG, addr, count,
+ buffer);
+}
+
+/** Wrapper function to ease writing of one AUX register. */
+int arc_jtag_write_aux_reg_one(struct arc_jtag *jtag_info, uint32_t addr,
+ uint32_t value)
+{
+ return arc_jtag_write_aux_reg(jtag_info, &addr, 1, &value);
+}
+
+/**
+ * Write AUX registers. addr is an array of addresses, and those addresses can
+ * be in any order, though it is recommended that they are in sequential order
+ * where possible, as this reduces number of JTAG commands to transfer.
+ *
+ * @param jtag_info
+ * @param addr Array of registers numbers.
+ * @param count Amount of registers in arrays.
+ * @param values Array of register values.
+ */
+int arc_jtag_write_aux_reg(struct arc_jtag *jtag_info, uint32_t *addr,
+ uint32_t count, const uint32_t *buffer)
+{
+ return arc_jtag_write_registers(jtag_info, ARC_JTAG_AUX_REG, addr, count,
+ buffer);
+}
+
+/** Wrapper function to ease reading of one AUX register. */
+int arc_jtag_read_aux_reg_one(struct arc_jtag *jtag_info, uint32_t addr,
+ uint32_t *value)
+{
+ return arc_jtag_read_aux_reg(jtag_info, &addr, 1, value);
+}
+
+/**
+ * Read AUX registers. addr is an array of addresses, and those addresses can
+ * be in any order, though it is recommended that they are in sequential order
+ * where possible, as this reduces number of JTAG commands to transfer.
+ *
+ * @param jtag_info
+ * @param addr Array of AUX register numbers.
+ * @param count Amount of registers in arrays.
+ * @param values Array of register values.
+ */
+int arc_jtag_read_aux_reg(struct arc_jtag *jtag_info, uint32_t *addr,
+ uint32_t count, uint32_t *buffer)
+{
+ return arc_jtag_read_registers(jtag_info, ARC_JTAG_AUX_REG, addr, count,
+ buffer);
+}
+
+/**
+ * Write a sequence of 4-byte words into target memory.
+ *
+ * We can write only 4byte words via JTAG, so any non-word writes should be
+ * handled at higher levels by read-modify-write.
+ *
+ * This function writes directly to the memory, leaving any caches (if there
+ * are any) in inconsistent state. It is responsibility of upper level to
+ * resolve this.
+ *
+ * @param jtag_info
+ * @param addr Address of first word to write into.
+ * @param count Amount of word to write.
+ * @param buffer Array to write into memory.
+ */
+int arc_jtag_write_memory(struct arc_jtag *jtag_info, uint32_t addr,
+ uint32_t count, const uint32_t *buffer)
+{
+ assert(jtag_info);
+ assert(buffer);
+
+ LOG_DEBUG("Writing to memory: addr=0x%08" PRIx32 ";count=%" PRIu32 ";buffer[0]=0x%08" PRIx32,
+ addr, count, *buffer);
+
+ /* No need to waste time on useless operations. */
+ if (!count)
+ return ERROR_OK;
+
+ /* We do not know where we come from. */
+ arc_jtag_enque_reset_transaction(jtag_info);
+
+ /* We want to write to memory. */
+ arc_jtag_enque_set_transaction(jtag_info, ARC_JTAG_WRITE_TO_MEMORY, TAP_DRPAUSE);
+
+ /* Set target memory address of the first word. */
+ arc_jtag_enque_write_ir(jtag_info, ARC_JTAG_ADDRESS_REG);
+ arc_jtag_enque_write_dr(jtag_info, addr, TAP_DRPAUSE);
+
+ /* Start sending words. Address is auto-incremented on 4bytes by HW. */
+ arc_jtag_enque_write_ir(jtag_info, ARC_JTAG_DATA_REG);
+
+ uint32_t i;
+ for (i = 0; i < count; i++)
+ arc_jtag_enque_write_dr(jtag_info, *(buffer + i), TAP_IDLE);
+
+ return jtag_execute_queue();
+}
+
+/**
+ * Read a sequence of 4-byte words from target memory.
+ *
+ * We can read only 4byte words via JTAG.
+ *
+ * This function read directly from the memory, so it can read invalid data if
+ * data cache hasn't been flushed before hand. It is responsibility of upper
+ * level to resolve this.
+ *
+ * @param jtag_info
+ * @param addr Address of first word to read from.
+ * @param count Amount of words to read.
+ * @param buffer Array of words to read into.
+ * @param slow_memory Whether this is a slow memory (DDR) or fast (CCM).
+ */
+int arc_jtag_read_memory(struct arc_jtag *jtag_info, uint32_t addr,
+ uint32_t count, uint32_t *buffer, bool slow_memory)
+{
+ uint8_t *data_buf;
+ uint32_t i;
+ int retval = ERROR_OK;
+
+
+ assert(jtag_info);
+ assert(jtag_info->tap);
+
+ LOG_DEBUG("Reading memory: addr=0x%" PRIx32 ";count=%" PRIu32 ";slow=%c",
+ addr, count, slow_memory ? 'Y' : 'N');
+
+ if (!count)
+ return ERROR_OK;
+
+ data_buf = calloc(sizeof(uint8_t), count * 4);
+ arc_jtag_enque_reset_transaction(jtag_info);
+
+ /* We are reading from memory. */
+ arc_jtag_enque_set_transaction(jtag_info, ARC_JTAG_READ_FROM_MEMORY, TAP_DRPAUSE);
+
+ /* Read data */
+ for (i = 0; i < count; i++) {
+ /* When several words are read at consequent addresses we can
+ * rely on ARC JTAG auto-incrementing address. That means that
+ * address can be set only once, for a first word. However it
+ * has been noted that at least in some cases when reading from
+ * DDR, JTAG returns 0 instead of a real value. To workaround
+ * this issue we need to do totally non-required address
+ * writes, which however resolve a problem by introducing
+ * delay. See STAR 9000832538... */
+ if (slow_memory || i == 0) {
+ /* Set address */
+ arc_jtag_enque_write_ir(jtag_info, ARC_JTAG_ADDRESS_REG);
+ arc_jtag_enque_write_dr(jtag_info, addr + i * 4, TAP_IDLE);
+
+ arc_jtag_enque_write_ir(jtag_info, ARC_JTAG_DATA_REG);
+ }
+ arc_jtag_enque_read_dr(jtag_info, data_buf + i * 4, TAP_IDLE);
+ }
+ retval = jtag_execute_queue();
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Failed to execute jtag queue: %d", retval);
+ retval = ERROR_FAIL;
+ goto exit;
+ }
+
+ /* Convert byte-buffers to host presentation. */
+ for (i = 0; i < count; i++)
+ buffer[i] = buf_get_u32(data_buf + 4*i, 0, 32);
+
+exit:
+ free(data_buf);
+
+ return retval;
+}
+
diff --git a/src/target/arc_jtag.h b/src/target/arc_jtag.h
new file mode 100644
index 0000000..99795f5
--- /dev/null
+++ b/src/target/arc_jtag.h
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * Copyright (C) 2013-2014,2019-2020 Synopsys, Inc. *
+ * Frank Dols <frank.dols@synopsys.com> *
+ * Mischa Jonker <mischa.jonker@synopsys.com> *
+ * Anton Kolesov <anton.kolesov@synopsys.com> *
+ * Evgeniy Didin <didin@synopsys.com> *
+ * *
+ * SPDX-License-Identifier: GPL-2.0-or-later *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ARC_JTAG_H
+#define OPENOCD_TARGET_ARC_JTAG_H
+
+#define ARC_TRANSACTION_CMD_REG 0x9 /* Command to perform */
+#define ARC_TRANSACTION_CMD_REG_LENGTH 4
+
+/* Jtag status register, value is placed in IR to read jtag status register */
+#define ARC_JTAG_STATUS_REG 0x8
+#define ARC_JTAG_ADDRESS_REG 0xA /* SoC address to access */
+#define ARC_JTAG_DATA_REG 0xB /* Data read/written from SoC */
+
+/* Jtag status register field */
+#define ARC_JTAG_STAT_RU 0x10
+
+/* ARC Jtag transactions */
+#define ARC_JTAG_WRITE_TO_MEMORY 0x0
+#define ARC_JTAG_WRITE_TO_CORE_REG 0x1
+#define ARC_JTAG_WRITE_TO_AUX_REG 0x2
+#define ARC_JTAG_CMD_NOP 0x3
+#define ARC_JTAG_READ_FROM_MEMORY 0x4
+#define ARC_JTAG_READ_FROM_CORE_REG 0x5
+#define ARC_JTAG_READ_FROM_AUX_REG 0x6
+
+#define ARC_JTAG_CORE_REG 0x0
+#define ARC_JTAG_AUX_REG 0x1
+
+
+struct arc_jtag {
+ struct jtag_tap *tap;
+ uint32_t cur_trans;
+};
+
+/* ----- Exported JTAG functions ------------------------------------------- */
+
+int arc_jtag_startup(struct arc_jtag *jtag_info);
+int arc_jtag_status(struct arc_jtag *const jtag_info, uint32_t *const value);
+
+int arc_jtag_write_core_reg(struct arc_jtag *jtag_info, uint32_t *addr,
+ uint32_t count, const uint32_t *buffer);
+int arc_jtag_read_core_reg(struct arc_jtag *jtag_info, uint32_t *addr,
+ uint32_t count, uint32_t *buffer);
+int arc_jtag_write_core_reg_one(struct arc_jtag *jtag_info, uint32_t addr,
+ const uint32_t buffer);
+int arc_jtag_read_core_reg_one(struct arc_jtag *jtag_info, uint32_t addr,
+ uint32_t *buffer);
+
+int arc_jtag_write_aux_reg(struct arc_jtag *jtag_info, uint32_t *addr,
+ uint32_t count, const uint32_t *buffer);
+int arc_jtag_write_aux_reg_one(struct arc_jtag *jtag_info, uint32_t addr,
+ uint32_t value);
+int arc_jtag_read_aux_reg(struct arc_jtag *jtag_info, uint32_t *addr,
+ uint32_t count, uint32_t *buffer);
+int arc_jtag_read_aux_reg_one(struct arc_jtag *jtag_info, uint32_t addr,
+ uint32_t *value);
+
+int arc_jtag_write_memory(struct arc_jtag *jtag_info, uint32_t addr,
+ uint32_t count, const uint32_t *buffer);
+int arc_jtag_read_memory(struct arc_jtag *jtag_info, uint32_t addr,
+ uint32_t count, uint32_t *buffer, bool slow_memory);
+#endif /* OPENOCD_TARGET_ARC_JTAG_H */
diff --git a/src/target/arc_mem.c b/src/target/arc_mem.c
new file mode 100644
index 0000000..e80bfb4
--- /dev/null
+++ b/src/target/arc_mem.c
@@ -0,0 +1,287 @@
+/***************************************************************************
+ * Copyright (C) 2013-2014,2019-2020 Synopsys, Inc. *
+ * Frank Dols <frank.dols@synopsys.com> *
+ * Mischa Jonker <mischa.jonker@synopsys.com> *
+ * Anton Kolesov <anton.kolesov@synopsys.com> *
+ * Evgeniy Didin <didin@synopsys.com> *
+ * *
+ * SPDX-License-Identifier: GPL-2.0-or-later *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "arc.h"
+
+/* ----- Supporting functions ---------------------------------------------- */
+static bool arc_mem_is_slow_memory(struct arc_common *arc, uint32_t addr,
+ uint32_t size, uint32_t count)
+{
+ uint32_t addr_end = addr + size * count;
+ /* `_end` field can overflow - it points to the first byte after the end,
+ * therefore if DCCM is right at the end of memory address space, then
+ * dccm_end will be 0. */
+ assert(addr_end >= addr || addr_end == 0);
+
+ return !((addr >= arc->dccm_start && addr_end <= arc->dccm_end) ||
+ (addr >= arc->iccm0_start && addr_end <= arc->iccm0_end) ||
+ (addr >= arc->iccm1_start && addr_end <= arc->iccm1_end));
+}
+
+/* Write word at word-aligned address */
+static int arc_mem_write_block32(struct target *target, uint32_t addr,
+ uint32_t count, void *buf)
+{
+ struct arc_common *arc = target_to_arc(target);
+
+ LOG_DEBUG("Write 4-byte memory block: addr=0x%08" PRIx32 ", count=%" PRIu32,
+ addr, count);
+
+ /* Check arguments */
+ assert(!(addr & 3));
+
+ /* No need to flush cache, because we don't read values from memory. */
+ CHECK_RETVAL(arc_jtag_write_memory(&arc->jtag_info, addr, count,
+ (uint32_t *)buf));
+
+ return ERROR_OK;
+}
+
+/* Write half-word at half-word-aligned address */
+static int arc_mem_write_block16(struct target *target, uint32_t addr,
+ uint32_t count, void *buf)
+{
+ struct arc_common *arc = target_to_arc(target);
+ uint32_t i;
+ uint32_t buffer_he;
+ uint8_t buffer_te[sizeof(uint32_t)];
+ uint8_t halfword_te[sizeof(uint16_t)];
+
+ LOG_DEBUG("Write 2-byte memory block: addr=0x%08" PRIx32 ", count=%" PRIu32,
+ addr, count);
+
+ /* Check arguments */
+ assert(!(addr & 1));
+
+ /* non-word writes are less common, than 4-byte writes, so I suppose we can
+ * allowe ourselves to write this in a cycle, instead of calling arc_jtag
+ * with count > 1. */
+ for (i = 0; i < count; i++) {
+ /* We can read only word at word-aligned address. Also *jtag_read_memory
+ * functions return data in host endianness, so host endianness !=
+ * target endianness we have to convert data back to target endianness,
+ * or bytes will be at the wrong places.So:
+ * 1) read word
+ * 2) convert to target endianness
+ * 3) make changes
+ * 4) convert back to host endianness
+ * 5) write word back to target.
+ */
+ bool is_slow_memory = arc_mem_is_slow_memory(arc,
+ (addr + i * sizeof(uint16_t)) & ~3u, 4, 1);
+ CHECK_RETVAL(arc_jtag_read_memory(&arc->jtag_info,
+ (addr + i * sizeof(uint16_t)) & ~3u, 1, &buffer_he,
+ is_slow_memory));
+ target_buffer_set_u32(target, buffer_te, buffer_he);
+
+ /* buf is in host endianness, convert to target */
+ target_buffer_set_u16(target, halfword_te, ((uint16_t *)buf)[i]);
+
+ memcpy(buffer_te + ((addr + i * sizeof(uint16_t)) & 3u),
+ halfword_te, sizeof(uint16_t));
+
+ buffer_he = target_buffer_get_u32(target, buffer_te);
+
+ CHECK_RETVAL(arc_jtag_write_memory(&arc->jtag_info,
+ (addr + i * sizeof(uint16_t)) & ~3u, 1, &buffer_he));
+ }
+
+ return ERROR_OK;
+}
+
+/* Write byte at address */
+static int arc_mem_write_block8(struct target *target, uint32_t addr,
+ uint32_t count, void *buf)
+{
+ struct arc_common *arc = target_to_arc(target);
+ uint32_t i;
+ uint32_t buffer_he;
+ uint8_t buffer_te[sizeof(uint32_t)];
+
+
+ LOG_DEBUG("Write 1-byte memory block: addr=0x%08" PRIx32 ", count=%" PRIu32,
+ addr, count);
+
+ /* non-word writes are less common, than 4-byte writes, so I suppose we can
+ * allowe ourselves to write this in a cycle, instead of calling arc_jtag
+ * with count > 1. */
+ for (i = 0; i < count; i++) {
+ /* See comment in arc_mem_write_block16 for details. Since it is a byte
+ * there is not need to convert write buffer to target endianness, but
+ * we still have to convert read buffer. */
+ CHECK_RETVAL(arc_jtag_read_memory(&arc->jtag_info, (addr + i) & ~3, 1, &buffer_he,
+ arc_mem_is_slow_memory(arc, (addr + i) & ~3, 4, 1)));
+ target_buffer_set_u32(target, buffer_te, buffer_he);
+ memcpy(buffer_te + ((addr + i) & 3), (uint8_t *)buf + i, 1);
+ buffer_he = target_buffer_get_u32(target, buffer_te);
+ CHECK_RETVAL(arc_jtag_write_memory(&arc->jtag_info, (addr + i) & ~3, 1, &buffer_he));
+ }
+
+ return ERROR_OK;
+}
+
+/* ----- Exported functions ------------------------------------------------ */
+int arc_mem_write(struct target *target, target_addr_t address, uint32_t size,
+ uint32_t count, const uint8_t *buffer)
+{
+ int retval = ERROR_OK;
+ void *tunnel = NULL;
+
+ LOG_DEBUG("address: 0x%08" TARGET_PRIxADDR ", size: %" PRIu32 ", count: %" PRIu32,
+ address, size, count);
+
+ if (target->state != TARGET_HALTED) {
+ LOG_WARNING("target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* sanitize arguments */
+ if (((size != 4) && (size != 2) && (size != 1)) || !(count) || !(buffer))
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u)))
+ return ERROR_TARGET_UNALIGNED_ACCESS;
+
+ /* correct endianess if we have word or hword access */
+ if (size > 1) {
+ /*
+ * arc_..._write_mem with size 4/2 requires uint32_t/uint16_t
+ * in host endianness, but byte array represents target endianness.
+ */
+ tunnel = calloc(1, count * size * sizeof(uint8_t));
+
+ if (!tunnel) {
+ LOG_ERROR("Unable to allocate memory");
+ return ERROR_FAIL;
+ }
+
+ switch (size) {
+ case 4:
+ target_buffer_get_u32_array(target, buffer, count,
+ (uint32_t *)tunnel);
+ break;
+ case 2:
+ target_buffer_get_u16_array(target, buffer, count,
+ (uint16_t *)tunnel);
+ break;
+ }
+ buffer = tunnel;
+ }
+
+ if (size == 4) {
+ retval = arc_mem_write_block32(target, address, count, (void *)buffer);
+ } else if (size == 2) {
+ /* We convert buffer from host endianness to target. But then in
+ * write_block16, we do the reverse. Is there a way to avoid this without
+ * breaking other cases? */
+ retval = arc_mem_write_block16(target, address, count, (void *)buffer);
+ } else {
+ retval = arc_mem_write_block8(target, address, count, (void *)buffer);
+ }
+
+ free(tunnel);
+
+ return retval;
+}
+
+static int arc_mem_read_block(struct target *target, target_addr_t addr,
+ uint32_t size, uint32_t count, void *buf)
+{
+ struct arc_common *arc = target_to_arc(target);
+
+ LOG_DEBUG("Read memory: addr=0x%08" TARGET_PRIxADDR ", size=%" PRIu32
+ ", count=%" PRIu32, addr, size, count);
+ assert(!(addr & 3));
+ assert(size == 4);
+
+ CHECK_RETVAL(arc_jtag_read_memory(&arc->jtag_info, addr, count, buf,
+ arc_mem_is_slow_memory(arc, addr, size, count)));
+
+ return ERROR_OK;
+}
+
+int arc_mem_read(struct target *target, target_addr_t address, uint32_t size,
+ uint32_t count, uint8_t *buffer)
+{
+ int retval = ERROR_OK;
+ void *tunnel_he;
+ uint8_t *tunnel_te;
+ uint32_t words_to_read, bytes_to_read;
+
+
+ LOG_DEBUG("Read memory: addr=0x%08" TARGET_PRIxADDR ", size=%" PRIu32
+ ", count=%" PRIu32, address, size, count);
+
+ if (target->state != TARGET_HALTED) {
+ LOG_WARNING("target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* Sanitize arguments */
+ if (((size != 4) && (size != 2) && (size != 1)) || !(count) || !(buffer))
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u)))
+ return ERROR_TARGET_UNALIGNED_ACCESS;
+
+ /* Reads are word-aligned, so padding might be required if count > 1.
+ * NB: +3 is a padding for the last word (in case it's not aligned;
+ * addr&3 is a padding for the first word (since address can be
+ * unaligned as well). */
+ bytes_to_read = (count * size + 3 + (address & 3u)) & ~3u;
+ words_to_read = bytes_to_read >> 2;
+ tunnel_he = calloc(1, bytes_to_read);
+ tunnel_te = calloc(1, bytes_to_read);
+
+ if (!tunnel_he || !tunnel_te) {
+ LOG_ERROR("Unable to allocate memory");
+ free(tunnel_he);
+ free(tunnel_te);
+ return ERROR_FAIL;
+ }
+
+ /* We can read only word-aligned words. */
+ retval = arc_mem_read_block(target, address & ~3u, sizeof(uint32_t),
+ words_to_read, tunnel_he);
+
+ /* arc_..._read_mem with size 4/2 returns uint32_t/uint16_t in host */
+ /* endianness, but byte array should represent target endianness */
+
+ if (ERROR_OK == retval) {
+ switch (size) {
+ case 4:
+ target_buffer_set_u32_array(target, buffer, count,
+ tunnel_he);
+ break;
+ case 2:
+ target_buffer_set_u32_array(target, tunnel_te,
+ words_to_read, tunnel_he);
+ /* Will that work properly with count > 1 and big endian? */
+ memcpy(buffer, tunnel_te + (address & 3u),
+ count * sizeof(uint16_t));
+ break;
+ case 1:
+ target_buffer_set_u32_array(target, tunnel_te,
+ words_to_read, tunnel_he);
+ /* Will that work properly with count > 1 and big endian? */
+ memcpy(buffer, tunnel_te + (address & 3u), count);
+ break;
+ }
+ }
+
+ free(tunnel_he);
+ free(tunnel_te);
+
+ return retval;
+}
diff --git a/src/target/arc_mem.h b/src/target/arc_mem.h
new file mode 100644
index 0000000..06e1c88
--- /dev/null
+++ b/src/target/arc_mem.h
@@ -0,0 +1,21 @@
+/***************************************************************************
+ * Copyright (C) 2013-2014,2019-2020 Synopsys, Inc. *
+ * Frank Dols <frank.dols@synopsys.com> *
+ * Anton Kolesov <anton.kolesov@synopsys.com> *
+ * Evgeniy Didin <didin@synopsys.com> *
+ * *
+ * SPDX-License-Identifier: GPL-2.0-or-later *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ARC_MEM_H
+#define OPENOCD_TARGET_ARC_MEM_H
+
+/* ----- Exported functions ------------------------------------------------ */
+
+int arc_mem_read(struct target *target, target_addr_t address, uint32_t size,
+ uint32_t count, uint8_t *buffer);
+int arc_mem_write(struct target *target, target_addr_t address, uint32_t size,
+ uint32_t count, const uint8_t *buffer);
+
+
+#endif /* OPENOCD_TARGET_ARC_MEM_H */
diff --git a/src/target/target.c b/src/target/target.c
index 688d318..1ba4e09 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -111,6 +111,7 @@ 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;
+extern struct target_type arcv2_target;
static struct target_type *target_types[] = {
&arm7tdmi_target,
@@ -146,6 +147,7 @@ static struct target_type *target_types[] = {
&riscv_target,
&mem_ap_target,
&esirisc_target,
+ &arcv2_target,
#if BUILD_TARGET64
&aarch64_target,
&mips_mips64_target,