This patch is an initial bump of ARC-specific code which implements the ARCv2 target(EMSK board) initializing routine and some basic remote connection/load/continue functionality. Changes: 03.12.2019: -Add return value checks. -Using static code analizer next fixes were made: Mem leak in functions: arc_jtag_read_memory,arc_jtag_read_memory, arc_jtag_write_registers, arc_jtag_read_registers, jim_arc_add_reg_type_flags, jim_arc_add_reg_type_struct, arc_build_reg_cache, arc_mem_read. Dead code in "arc_mem_read"; In arc_save_context, arc_restore_context correct arguments in"memset" calls. In "build_bcr_reg_cache", "arc_build_reg_cache" check if list is not empty. 29.12.2019 -Moved code from arc_v2.c to arc.c -Added checks of the result of calloc/malloc calls -Reworked arc_cmd.c: replaced spagetty code with functions -Moved to one style in if statements - to "if(!bla)" -Changed Licence headers 22.01.2020 -Removed unused variables in arc_common -Renamed register operation functions -Introduced arc_deinit_target function -Fixed interrupt handling in halt/resume: * add irq_state field in arc_common * fix irq enable/disable calls ( now STATUS32 register is used) -Switched from buf_set(get)_us32() usage to target_buffer_set(get)_u32() -Made some cleanup 30.01.2020 -Removed redundant arc_register struct, moved target link to arc_reg_desc -Introduced link to BCR reg cache in arc_common for freeing memory. -Now arc_deinit_target frees all arc-related allocated memory. Valgrind shows no memory leaks. -Inroduced arch description in arc.c 01.02.2020 -Remove small memory allocations in arc_init_reg. Instead created reg_value and feature fields in arc_reg_desc. -Add return value for arc_init_reg() func. -Replaced some integer constants(61,62,63) with defines. -Removed redundant conversions in arc_reg_get_field(). -Moved iccm/dccm configuration code from arc_configure() to separate functions. 19.02.2020 -Change sizeof(struct) to sizeof(*ptr) in allocations -Changed if/while(ptr != NULL) to if/while(ptr) -Removed unused variables from struct arc_jtag -Add additional structs to arc_reg_data_type to reduce amount of memory allocations calls and simplifying memory freeing. -Add helper arc_reg_bitfield_t struct which includes reg_data_type_bitfield object and char[] name. Reduces memory allocations calls. -Add limit for reg_type/reg_type_field names(20 symbols). -Add in jim_arc_add_reg_type*() functions additional argnument checks(amount of field/name size). -In jim_arc_add_reg_type*() reduced amount of memory allocations. -Cleanup of jim_arc_add_reg_type*() functions. -For commands update ".usage" fields according docopt. -Cleanup in arc_jtag.c -Renamed functions which require jtag_exeutre_queue() to arc_jtag_enque_*() -Add arc_jtag_enque_register_rw() function, which r/w to jtag ir/dr regs during regiter r/w. 24.02: -Change include guards in arc* files according coding style -Remove _t suffix in struct arc_reg_bitfield_t -Some cleanup Change-Id: I6ab0e82b12e6ddb683c9d13dfb7dd6f49a30cb9f Signed-off-by: Evgeniy Didin <didin@synopsys.com> Cc: Alexey Brodkin <abrodkin@synopsys.com> Reviewed-on: http://openocd.zylin.com/5332 Tested-by: jenkins Reviewed-by: Oleksij Rempel <linux@rempel-privat.de>jim
@@ -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 |
@@ -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 */ |
@@ -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 | |||
}; |
@@ -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 */ |
@@ -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; | |||
} | |||
@@ -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 */ |
@@ -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; | |||
} |
@@ -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 */ |
@@ -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, | |||