diff options
author | Evgeniy Didin <didin@synopsys.com> | 2020-01-27 15:22:27 +0300 |
---|---|---|
committer | Oleksij Rempel <linux@rempel-privat.de> | 2020-02-27 06:46:51 +0000 |
commit | 9ee9bdd2f9e69df816d313d23b50a563c0869428 (patch) | |
tree | 48b83b0ca14b1941ff67bda0b562bc86a869759e /src/target/arc_mem.c | |
parent | 3bfe4926632d458da449f0438db6949c75b7af59 (diff) | |
download | riscv-openocd-9ee9bdd2f9e69df816d313d23b50a563c0869428.zip riscv-openocd-9ee9bdd2f9e69df816d313d23b50a563c0869428.tar.gz riscv-openocd-9ee9bdd2f9e69df816d313d23b50a563c0869428.tar.bz2 |
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>
Diffstat (limited to 'src/target/arc_mem.c')
-rw-r--r-- | src/target/arc_mem.c | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/src/target/arc_mem.c b/src/target/arc_mem.c new file mode 100644 index 0000000..e80bfb4 --- /dev/null +++ b/src/target/arc_mem.c @@ -0,0 +1,287 @@ +/*************************************************************************** + * Copyright (C) 2013-2014,2019-2020 Synopsys, Inc. * + * Frank Dols <frank.dols@synopsys.com> * + * Mischa Jonker <mischa.jonker@synopsys.com> * + * Anton Kolesov <anton.kolesov@synopsys.com> * + * Evgeniy Didin <didin@synopsys.com> * + * * + * SPDX-License-Identifier: GPL-2.0-or-later * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "arc.h" + +/* ----- Supporting functions ---------------------------------------------- */ +static bool arc_mem_is_slow_memory(struct arc_common *arc, uint32_t addr, + uint32_t size, uint32_t count) +{ + uint32_t addr_end = addr + size * count; + /* `_end` field can overflow - it points to the first byte after the end, + * therefore if DCCM is right at the end of memory address space, then + * dccm_end will be 0. */ + assert(addr_end >= addr || addr_end == 0); + + return !((addr >= arc->dccm_start && addr_end <= arc->dccm_end) || + (addr >= arc->iccm0_start && addr_end <= arc->iccm0_end) || + (addr >= arc->iccm1_start && addr_end <= arc->iccm1_end)); +} + +/* Write word at word-aligned address */ +static int arc_mem_write_block32(struct target *target, uint32_t addr, + uint32_t count, void *buf) +{ + struct arc_common *arc = target_to_arc(target); + + LOG_DEBUG("Write 4-byte memory block: addr=0x%08" PRIx32 ", count=%" PRIu32, + addr, count); + + /* Check arguments */ + assert(!(addr & 3)); + + /* No need to flush cache, because we don't read values from memory. */ + CHECK_RETVAL(arc_jtag_write_memory(&arc->jtag_info, addr, count, + (uint32_t *)buf)); + + return ERROR_OK; +} + +/* Write half-word at half-word-aligned address */ +static int arc_mem_write_block16(struct target *target, uint32_t addr, + uint32_t count, void *buf) +{ + struct arc_common *arc = target_to_arc(target); + uint32_t i; + uint32_t buffer_he; + uint8_t buffer_te[sizeof(uint32_t)]; + uint8_t halfword_te[sizeof(uint16_t)]; + + LOG_DEBUG("Write 2-byte memory block: addr=0x%08" PRIx32 ", count=%" PRIu32, + addr, count); + + /* Check arguments */ + assert(!(addr & 1)); + + /* non-word writes are less common, than 4-byte writes, so I suppose we can + * allowe ourselves to write this in a cycle, instead of calling arc_jtag + * with count > 1. */ + for (i = 0; i < count; i++) { + /* We can read only word at word-aligned address. Also *jtag_read_memory + * functions return data in host endianness, so host endianness != + * target endianness we have to convert data back to target endianness, + * or bytes will be at the wrong places.So: + * 1) read word + * 2) convert to target endianness + * 3) make changes + * 4) convert back to host endianness + * 5) write word back to target. + */ + bool is_slow_memory = arc_mem_is_slow_memory(arc, + (addr + i * sizeof(uint16_t)) & ~3u, 4, 1); + CHECK_RETVAL(arc_jtag_read_memory(&arc->jtag_info, + (addr + i * sizeof(uint16_t)) & ~3u, 1, &buffer_he, + is_slow_memory)); + target_buffer_set_u32(target, buffer_te, buffer_he); + + /* buf is in host endianness, convert to target */ + target_buffer_set_u16(target, halfword_te, ((uint16_t *)buf)[i]); + + memcpy(buffer_te + ((addr + i * sizeof(uint16_t)) & 3u), + halfword_te, sizeof(uint16_t)); + + buffer_he = target_buffer_get_u32(target, buffer_te); + + CHECK_RETVAL(arc_jtag_write_memory(&arc->jtag_info, + (addr + i * sizeof(uint16_t)) & ~3u, 1, &buffer_he)); + } + + return ERROR_OK; +} + +/* Write byte at address */ +static int arc_mem_write_block8(struct target *target, uint32_t addr, + uint32_t count, void *buf) +{ + struct arc_common *arc = target_to_arc(target); + uint32_t i; + uint32_t buffer_he; + uint8_t buffer_te[sizeof(uint32_t)]; + + + LOG_DEBUG("Write 1-byte memory block: addr=0x%08" PRIx32 ", count=%" PRIu32, + addr, count); + + /* non-word writes are less common, than 4-byte writes, so I suppose we can + * allowe ourselves to write this in a cycle, instead of calling arc_jtag + * with count > 1. */ + for (i = 0; i < count; i++) { + /* See comment in arc_mem_write_block16 for details. Since it is a byte + * there is not need to convert write buffer to target endianness, but + * we still have to convert read buffer. */ + CHECK_RETVAL(arc_jtag_read_memory(&arc->jtag_info, (addr + i) & ~3, 1, &buffer_he, + arc_mem_is_slow_memory(arc, (addr + i) & ~3, 4, 1))); + target_buffer_set_u32(target, buffer_te, buffer_he); + memcpy(buffer_te + ((addr + i) & 3), (uint8_t *)buf + i, 1); + buffer_he = target_buffer_get_u32(target, buffer_te); + CHECK_RETVAL(arc_jtag_write_memory(&arc->jtag_info, (addr + i) & ~3, 1, &buffer_he)); + } + + return ERROR_OK; +} + +/* ----- Exported functions ------------------------------------------------ */ +int arc_mem_write(struct target *target, target_addr_t address, uint32_t size, + uint32_t count, const uint8_t *buffer) +{ + int retval = ERROR_OK; + void *tunnel = NULL; + + LOG_DEBUG("address: 0x%08" TARGET_PRIxADDR ", size: %" PRIu32 ", count: %" PRIu32, + address, size, count); + + if (target->state != TARGET_HALTED) { + LOG_WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* sanitize arguments */ + if (((size != 4) && (size != 2) && (size != 1)) || !(count) || !(buffer)) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u))) + return ERROR_TARGET_UNALIGNED_ACCESS; + + /* correct endianess if we have word or hword access */ + if (size > 1) { + /* + * arc_..._write_mem with size 4/2 requires uint32_t/uint16_t + * in host endianness, but byte array represents target endianness. + */ + tunnel = calloc(1, count * size * sizeof(uint8_t)); + + if (!tunnel) { + LOG_ERROR("Unable to allocate memory"); + return ERROR_FAIL; + } + + switch (size) { + case 4: + target_buffer_get_u32_array(target, buffer, count, + (uint32_t *)tunnel); + break; + case 2: + target_buffer_get_u16_array(target, buffer, count, + (uint16_t *)tunnel); + break; + } + buffer = tunnel; + } + + if (size == 4) { + retval = arc_mem_write_block32(target, address, count, (void *)buffer); + } else if (size == 2) { + /* We convert buffer from host endianness to target. But then in + * write_block16, we do the reverse. Is there a way to avoid this without + * breaking other cases? */ + retval = arc_mem_write_block16(target, address, count, (void *)buffer); + } else { + retval = arc_mem_write_block8(target, address, count, (void *)buffer); + } + + free(tunnel); + + return retval; +} + +static int arc_mem_read_block(struct target *target, target_addr_t addr, + uint32_t size, uint32_t count, void *buf) +{ + struct arc_common *arc = target_to_arc(target); + + LOG_DEBUG("Read memory: addr=0x%08" TARGET_PRIxADDR ", size=%" PRIu32 + ", count=%" PRIu32, addr, size, count); + assert(!(addr & 3)); + assert(size == 4); + + CHECK_RETVAL(arc_jtag_read_memory(&arc->jtag_info, addr, count, buf, + arc_mem_is_slow_memory(arc, addr, size, count))); + + return ERROR_OK; +} + +int arc_mem_read(struct target *target, target_addr_t address, uint32_t size, + uint32_t count, uint8_t *buffer) +{ + int retval = ERROR_OK; + void *tunnel_he; + uint8_t *tunnel_te; + uint32_t words_to_read, bytes_to_read; + + + LOG_DEBUG("Read memory: addr=0x%08" TARGET_PRIxADDR ", size=%" PRIu32 + ", count=%" PRIu32, address, size, count); + + if (target->state != TARGET_HALTED) { + LOG_WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Sanitize arguments */ + if (((size != 4) && (size != 2) && (size != 1)) || !(count) || !(buffer)) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u))) + return ERROR_TARGET_UNALIGNED_ACCESS; + + /* Reads are word-aligned, so padding might be required if count > 1. + * NB: +3 is a padding for the last word (in case it's not aligned; + * addr&3 is a padding for the first word (since address can be + * unaligned as well). */ + bytes_to_read = (count * size + 3 + (address & 3u)) & ~3u; + words_to_read = bytes_to_read >> 2; + tunnel_he = calloc(1, bytes_to_read); + tunnel_te = calloc(1, bytes_to_read); + + if (!tunnel_he || !tunnel_te) { + LOG_ERROR("Unable to allocate memory"); + free(tunnel_he); + free(tunnel_te); + return ERROR_FAIL; + } + + /* We can read only word-aligned words. */ + retval = arc_mem_read_block(target, address & ~3u, sizeof(uint32_t), + words_to_read, tunnel_he); + + /* arc_..._read_mem with size 4/2 returns uint32_t/uint16_t in host */ + /* endianness, but byte array should represent target endianness */ + + if (ERROR_OK == retval) { + switch (size) { + case 4: + target_buffer_set_u32_array(target, buffer, count, + tunnel_he); + break; + case 2: + target_buffer_set_u32_array(target, tunnel_te, + words_to_read, tunnel_he); + /* Will that work properly with count > 1 and big endian? */ + memcpy(buffer, tunnel_te + (address & 3u), + count * sizeof(uint16_t)); + break; + case 1: + target_buffer_set_u32_array(target, tunnel_te, + words_to_read, tunnel_he); + /* Will that work properly with count > 1 and big endian? */ + memcpy(buffer, tunnel_te + (address & 3u), count); + break; + } + } + + free(tunnel_he); + free(tunnel_te); + + return retval; +} |