diff options
-rw-r--r-- | src/target/Makefile.am | 13 | ||||
-rw-r--r-- | src/target/arc.c | 1339 | ||||
-rw-r--r-- | src/target/arc.h | 212 | ||||
-rw-r--r-- | src/target/arc_cmd.c | 977 | ||||
-rw-r--r-- | src/target/arc_cmd.h | 16 | ||||
-rw-r--r-- | src/target/arc_jtag.c | 542 | ||||
-rw-r--r-- | src/target/arc_jtag.h | 70 | ||||
-rw-r--r-- | src/target/arc_mem.c | 287 | ||||
-rw-r--r-- | src/target/arc_mem.h | 21 | ||||
-rw-r--r-- | src/target/target.c | 2 |
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 = ®_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 = ®_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, ®_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, ®_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, ®_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] = ®_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] = ®_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, ®num)); + + /* 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, ®num)); + + 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, ®num)); + 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, ®num)); + 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, ®_name, ®_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, ®_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, |