1 /***************************************************************************
2 * Copyright (C) 2013-2014,2019-2020 Synopsys, Inc. *
3 * Frank Dols <frank.dols@synopsys.com> *
4 * Mischa Jonker <mischa.jonker@synopsys.com> *
5 * Anton Kolesov <anton.kolesov@synopsys.com> *
6 * Evgeniy Didin <didin@synopsys.com> *
8 * SPDX-License-Identifier: GPL-2.0-or-later *
9 ***************************************************************************/
17 /* ----- Supporting functions ---------------------------------------------- */
18 static bool arc_mem_is_slow_memory(struct arc_common *arc, uint32_t addr,
19 uint32_t size, uint32_t count)
21 uint32_t addr_end = addr + size * count;
22 /* `_end` field can overflow - it points to the first byte after the end,
23 * therefore if DCCM is right at the end of memory address space, then
24 * dccm_end will be 0. */
25 assert(addr_end >= addr || addr_end == 0);
27 return !((addr >= arc->dccm_start && addr_end <= arc->dccm_end) ||
28 (addr >= arc->iccm0_start && addr_end <= arc->iccm0_end) ||
29 (addr >= arc->iccm1_start && addr_end <= arc->iccm1_end));
32 /* Write word at word-aligned address */
33 static int arc_mem_write_block32(struct target *target, uint32_t addr,
34 uint32_t count, void *buf)
36 struct arc_common *arc = target_to_arc(target);
38 LOG_DEBUG("Write 4-byte memory block: addr=0x%08" PRIx32 ", count=%" PRIu32,
44 /* No need to flush cache, because we don't read values from memory. */
45 CHECK_RETVAL(arc_jtag_write_memory(&arc->jtag_info, addr, count,
51 /* Write half-word at half-word-aligned address */
52 static int arc_mem_write_block16(struct target *target, uint32_t addr,
53 uint32_t count, void *buf)
55 struct arc_common *arc = target_to_arc(target);
58 uint8_t buffer_te[sizeof(uint32_t)];
59 uint8_t halfword_te[sizeof(uint16_t)];
61 LOG_DEBUG("Write 2-byte memory block: addr=0x%08" PRIx32 ", count=%" PRIu32,
67 /* non-word writes are less common, than 4-byte writes, so I suppose we can
68 * allowe ourselves to write this in a cycle, instead of calling arc_jtag
70 for (i = 0; i < count; i++) {
71 /* We can read only word at word-aligned address. Also *jtag_read_memory
72 * functions return data in host endianness, so host endianness !=
73 * target endianness we have to convert data back to target endianness,
74 * or bytes will be at the wrong places.So:
76 * 2) convert to target endianness
78 * 4) convert back to host endianness
79 * 5) write word back to target.
81 bool is_slow_memory = arc_mem_is_slow_memory(arc,
82 (addr + i * sizeof(uint16_t)) & ~3u, 4, 1);
83 CHECK_RETVAL(arc_jtag_read_memory(&arc->jtag_info,
84 (addr + i * sizeof(uint16_t)) & ~3u, 1, &buffer_he,
86 target_buffer_set_u32(target, buffer_te, buffer_he);
88 /* buf is in host endianness, convert to target */
89 target_buffer_set_u16(target, halfword_te, ((uint16_t *)buf)[i]);
91 memcpy(buffer_te + ((addr + i * sizeof(uint16_t)) & 3u),
92 halfword_te, sizeof(uint16_t));
94 buffer_he = target_buffer_get_u32(target, buffer_te);
96 CHECK_RETVAL(arc_jtag_write_memory(&arc->jtag_info,
97 (addr + i * sizeof(uint16_t)) & ~3u, 1, &buffer_he));
103 /* Write byte at address */
104 static int arc_mem_write_block8(struct target *target, uint32_t addr,
105 uint32_t count, void *buf)
107 struct arc_common *arc = target_to_arc(target);
110 uint8_t buffer_te[sizeof(uint32_t)];
113 LOG_DEBUG("Write 1-byte memory block: addr=0x%08" PRIx32 ", count=%" PRIu32,
116 /* non-word writes are less common, than 4-byte writes, so I suppose we can
117 * allowe ourselves to write this in a cycle, instead of calling arc_jtag
119 for (i = 0; i < count; i++) {
120 /* See comment in arc_mem_write_block16 for details. Since it is a byte
121 * there is not need to convert write buffer to target endianness, but
122 * we still have to convert read buffer. */
123 CHECK_RETVAL(arc_jtag_read_memory(&arc->jtag_info, (addr + i) & ~3, 1, &buffer_he,
124 arc_mem_is_slow_memory(arc, (addr + i) & ~3, 4, 1)));
125 target_buffer_set_u32(target, buffer_te, buffer_he);
126 memcpy(buffer_te + ((addr + i) & 3), (uint8_t *)buf + i, 1);
127 buffer_he = target_buffer_get_u32(target, buffer_te);
128 CHECK_RETVAL(arc_jtag_write_memory(&arc->jtag_info, (addr + i) & ~3, 1, &buffer_he));
134 /* ----- Exported functions ------------------------------------------------ */
135 int arc_mem_write(struct target *target, target_addr_t address, uint32_t size,
136 uint32_t count, const uint8_t *buffer)
138 int retval = ERROR_OK;
141 LOG_DEBUG("address: 0x%08" TARGET_PRIxADDR ", size: %" PRIu32 ", count: %" PRIu32,
142 address, size, count);
144 if (target->state != TARGET_HALTED) {
145 LOG_WARNING("target not halted");
146 return ERROR_TARGET_NOT_HALTED;
149 /* sanitize arguments */
150 if (((size != 4) && (size != 2) && (size != 1)) || !(count) || !(buffer))
151 return ERROR_COMMAND_SYNTAX_ERROR;
153 if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u)))
154 return ERROR_TARGET_UNALIGNED_ACCESS;
156 /* correct endianess if we have word or hword access */
159 * arc_..._write_mem with size 4/2 requires uint32_t/uint16_t
160 * in host endianness, but byte array represents target endianness.
162 tunnel = calloc(1, count * size * sizeof(uint8_t));
165 LOG_ERROR("Unable to allocate memory");
171 target_buffer_get_u32_array(target, buffer, count,
175 target_buffer_get_u16_array(target, buffer, count,
183 retval = arc_mem_write_block32(target, address, count, (void *)buffer);
184 } else if (size == 2) {
185 /* We convert buffer from host endianness to target. But then in
186 * write_block16, we do the reverse. Is there a way to avoid this without
187 * breaking other cases? */
188 retval = arc_mem_write_block16(target, address, count, (void *)buffer);
190 retval = arc_mem_write_block8(target, address, count, (void *)buffer);
198 static int arc_mem_read_block(struct target *target, target_addr_t addr,
199 uint32_t size, uint32_t count, void *buf)
201 struct arc_common *arc = target_to_arc(target);
203 LOG_DEBUG("Read memory: addr=0x%08" TARGET_PRIxADDR ", size=%" PRIu32
204 ", count=%" PRIu32, addr, size, count);
208 CHECK_RETVAL(arc_jtag_read_memory(&arc->jtag_info, addr, count, buf,
209 arc_mem_is_slow_memory(arc, addr, size, count)));
214 int arc_mem_read(struct target *target, target_addr_t address, uint32_t size,
215 uint32_t count, uint8_t *buffer)
217 int retval = ERROR_OK;
220 uint32_t words_to_read, bytes_to_read;
223 LOG_DEBUG("Read memory: addr=0x%08" TARGET_PRIxADDR ", size=%" PRIu32
224 ", count=%" PRIu32, address, size, count);
226 if (target->state != TARGET_HALTED) {
227 LOG_WARNING("target not halted");
228 return ERROR_TARGET_NOT_HALTED;
231 /* Sanitize arguments */
232 if (((size != 4) && (size != 2) && (size != 1)) || !(count) || !(buffer))
233 return ERROR_COMMAND_SYNTAX_ERROR;
235 if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u)))
236 return ERROR_TARGET_UNALIGNED_ACCESS;
238 /* Reads are word-aligned, so padding might be required if count > 1.
239 * NB: +3 is a padding for the last word (in case it's not aligned;
240 * addr&3 is a padding for the first word (since address can be
241 * unaligned as well). */
242 bytes_to_read = (count * size + 3 + (address & 3u)) & ~3u;
243 words_to_read = bytes_to_read >> 2;
244 tunnel_he = calloc(1, bytes_to_read);
245 tunnel_te = calloc(1, bytes_to_read);
247 if (!tunnel_he || !tunnel_te) {
248 LOG_ERROR("Unable to allocate memory");
254 /* We can read only word-aligned words. */
255 retval = arc_mem_read_block(target, address & ~3u, sizeof(uint32_t),
256 words_to_read, tunnel_he);
258 /* arc_..._read_mem with size 4/2 returns uint32_t/uint16_t in host */
259 /* endianness, but byte array should represent target endianness */
261 if (ERROR_OK == retval) {
264 target_buffer_set_u32_array(target, buffer, count,
268 target_buffer_set_u32_array(target, tunnel_te,
269 words_to_read, tunnel_he);
270 /* Will that work properly with count > 1 and big endian? */
271 memcpy(buffer, tunnel_te + (address & 3u),
272 count * sizeof(uint16_t));
275 target_buffer_set_u32_array(target, tunnel_te,
276 words_to_read, tunnel_he);
277 /* Will that work properly with count > 1 and big endian? */
278 memcpy(buffer, tunnel_te + (address & 3u), count);