aboutsummaryrefslogtreecommitdiff
path: root/src/target/arc_mem.c
diff options
context:
space:
mode:
authorEvgeniy Didin <didin@synopsys.com>2020-01-27 15:22:27 +0300
committerOleksij Rempel <linux@rempel-privat.de>2020-02-27 06:46:51 +0000
commit9ee9bdd2f9e69df816d313d23b50a563c0869428 (patch)
tree48b83b0ca14b1941ff67bda0b562bc86a869759e /src/target/arc_mem.c
parent3bfe4926632d458da449f0438db6949c75b7af59 (diff)
downloadriscv-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.c287
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;
+}