Introduce ARCv2 architecture related code
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>
This commit is contained in:
parent
3bfe492663
commit
9ee9bdd2f9
|
@ -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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue