This is an automated email from Gerrit.

Marc Schink (d...@zapb.de) just uploaded a new patch set to Gerrit, which you 
can find at http://openocd.zylin.com/6307

-- gerrit

commit eb896d13e3d52dc4d49686bb5d214286cbe5b45a
Author: Marc Schink <d...@zapb.de>
Date:   Mon Jun 7 14:40:11 2021 +0200

    target/tcl: Add 'read_memory' and 'write_memory'
    
    These functions are meant as replacement for 'mem2array' and
    'array2mem'.
    
    The main benefits of these new functions are:
    
     * They do not use Tcl arrays but lists which makes it easier
       to parse (generate) the data. See the Python Tcl RPC code
       in contrib as a negative example.
    
     * They do not operate on Tcl variables but instead return (accept)
       the Tcl list directly. This makes the C and Tcl code base
       smaller and cleaner.
    
     * The code is slightly more performant when reading / writing
       large amount of data. Tested with a simple Python Tcl RPC
       benchmark.
    
    Change-Id: Ibd6ece3360c0d002abaadc37f078b10a8bb606f8
    Signed-off-by: Marc Schink <d...@zapb.de>

diff --git a/doc/openocd.texi b/doc/openocd.texi
index fc44833..27af1a2 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -4779,6 +4779,45 @@ and neither store nor return those values.
 @end itemize
 @end deffn
 
+@deffn {Command} {$target_name write_memory} aiddress width data ['phys']
+This function provides an efficient way to write to the target memory from a 
Tcl
+script.
+
+@itemize
+@item @var{address} ... target memory address
+@item @var{width} ... memory access bit size, can be 8, 16, 32 or 64
+@item @var{data} ... Tcl list with the elements to write
+@item ['phys'] ... treat the memory address as physical instead of virtual 
address
+@end itemize
+
+For example, the following command writes two 32 bit words into the target
+memory at address 0x20000000:
+
+@example
+write_memory 0x20000000 32 @{0xdeadbeef 0x00230500@}
+@end example
+@end deffn
+
+@deffn {Command} {$target_name read_memory} address width count ['phys']
+This function provides an efficient way to read the target memory from a Tcl
+script.
+A Tcl list containing the requested memory elements is returned by this 
function.
+
+@itemize
+@item @var{address} ... target memory address
+@item @var{width} ... memory access bit size, can be 8, 16, 32 or 64
+@item @var{count} ... number of elements to read
+@item ['phys'] ... treat the memory address as physical instead of virtual 
address
+@end itemize
+
+For example, the following command reads two 32 bit words from the target
+memory at address 0x20000000:
+
+@example
+read_memory 0x20000000 32 2
+@end example
+@end deffn
+
 @deffn {Command} {$target_name cget} queryparm
 Each configuration parameter accepted by
 @command{$target_name configure}
@@ -11123,12 +11162,22 @@ By "low-level," we mean commands that a human would 
typically not
 invoke directly.
 
 @itemize @bullet
+@item @b{read_memory} <@var{address}> <@var{width}> <@var{count}> ['phys']
+
+Read memory and return a Tcl list.
+
+@item @b{write_memory} <@var{address}> <@var{width}> <@var{data}> ['phys']
+
+Write a Tcl list of numbers to memory.
 @item @b{mem2array} <@var{varname}> <@var{width}> <@var{addr}> <@var{nelems}>
 
-Read memory and return as a Tcl array for script processing
+Read memory and return as a Tcl array for script processing.
+@emph{This command is deprecated, use @b{read_memory} instead.}
+
 @item @b{array2mem} <@var{varname}> <@var{width}> <@var{addr}> <@var{nelems}>
 
-Convert a Tcl array to memory locations and write the values
+Convert a Tcl array to memory locations and write the values.
+@emph{This command is deprecated, use @b{write_memory} instead.}
 @item @b{flash banks} <@var{driver}> <@var{base}> <@var{size}> 
<@var{chip_width}> <@var{bus_width}> <@var{target}> [@option{driver options} 
...]
 
 Return information about the flash banks
diff --git a/src/target/target.c b/src/target/target.c
index a788a68..7e9a988 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -4556,6 +4556,179 @@ static int target_mem2array(Jim_Interp *interp, struct 
target *target, int argc,
        return e;
 }
 
+static int target_jim_read_memory(Jim_Interp *interp, struct target *target,
+               int argc, Jim_Obj * const *argv)
+{
+       /*
+        * argv[0] = memory address
+        * argv[1] = desired element width in bits
+        * argv[2] = number of elements to read
+        * argv[3] = optional "phys"
+        */
+
+       if (argc < 3 || argc > 4) {
+               Jim_WrongNumArgs(interp, 0, argv, "address width count [phys]");
+               return JIM_ERR;
+       }
+
+       /* Arg 0: Memory address. */
+       jim_wide wide_addr;
+       int e;
+       e = Jim_GetWide(interp, argv[0], &wide_addr);
+
+       if (e != JIM_OK)
+               return e;
+
+       target_addr_t addr = (target_addr_t)wide_addr;
+
+       /* Arg 1: Bit width of one element. */
+       long l;
+       e = Jim_GetLong(interp, argv[1], &l);
+
+       if (e != JIM_OK)
+               return e;
+
+       const unsigned int width_bits = l;
+
+       /* Arg 2: Number of elements to read. */
+       e = Jim_GetLong(interp, argv[2], &l);
+
+       if (e != JIM_OK)
+               return e;
+
+       size_t count = l;
+
+       /* Arg 4: Optional 'phys'. */
+       bool is_phys = false;
+
+       if (argc > 3) {
+               int str_len;
+               const char *phys = Jim_GetString(argv[3], &str_len);
+
+               if (!strncmp(phys, "phys", str_len))
+                       is_phys = true;
+               else
+                       return JIM_ERR;
+       }
+
+       switch (width_bits) {
+       case 8:
+       case 16:
+       case 32:
+       case 64:
+               break;
+       default:
+               Jim_SetResultString(interp, "invalid width param, must be 8, 
16, 32 or 64", -1);
+               return JIM_ERR;
+       }
+
+       const unsigned int width = width_bits / 8;
+
+       if ((addr + (count * width)) < addr) {
+               Jim_SetResultString(interp, "read_memory: addr + count wraps to 
zero", -1);
+               return JIM_ERR;
+       }
+
+       if (count > 65536) {
+               Jim_SetResultString(interp, "read_memory: too large read 
request, exeeds 64K elements", -1);
+               return JIM_ERR;
+       }
+
+       if ((width == 1) ||
+               ((width == 2) && ((addr & 1) == 0)) ||
+               ((width == 4) && ((addr & 3) == 0)) ||
+               ((width == 8) && ((addr & 7) == 0))) {
+               /* Alignment is correct. */
+       } else {
+               char buf[128];
+               Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+               snprintf(buf, sizeof(buf), "read_memory: " TARGET_ADDR_FMT " is 
not aligned for %u byte reads",
+                       addr, width);
+               Jim_AppendStrings(interp, Jim_GetResult(interp), buf, NULL);
+               return JIM_ERR;
+       }
+
+       const size_t buffersize = 4096;
+       uint8_t *buffer = malloc(buffersize);
+
+       if (!buffer)
+               return JIM_ERR;
+
+       Jim_Obj *result_list = Jim_NewListObj(interp, NULL, 0);
+
+       while (count > 0) {
+               const unsigned int max_chunk_len = buffersize / width;
+               const size_t chunk_len = MIN(count, max_chunk_len);
+
+               int retval;
+
+               if (is_phys)
+                       retval = target_read_phys_memory(target, addr, width, 
chunk_len, buffer);
+               else
+                       retval = target_read_memory(target, addr, width, 
chunk_len, buffer);
+
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("read_memory: read at " TARGET_ADDR_FMT " 
with width=%u and count=%zu failed",
+                               addr, width_bits, chunk_len);
+                       Jim_SetResultString(interp, "read_memory: failed to 
read memory", -1);
+                       e = JIM_ERR;
+                       break;
+               } else {
+                       uint64_t v = 0;
+
+                       for (size_t i = 0; i < chunk_len ; i++) {
+                               switch (width) {
+                               case 8:
+                                       v = target_buffer_get_u64(target, 
&buffer[i * width]);
+                                       break;
+                               case 4:
+                                       v = target_buffer_get_u32(target, 
&buffer[i * width]);
+                                       break;
+                               case 2:
+                                       v = target_buffer_get_u16(target, 
&buffer[i * width]);
+                                       break;
+                               case 1:
+                                       v = buffer[i] & 0x0ff;
+                                       break;
+                               }
+
+                               Jim_ListAppendElement(interp, result_list,
+                                       Jim_NewWideObj(interp, v));
+                       }
+
+                       count -= chunk_len;
+                       addr += chunk_len * width;
+               }
+       }
+
+       free(buffer);
+
+       if (e != JIM_OK) {
+               Jim_DecrRefCount(interp, result_list);
+               return e;
+       }
+
+       Jim_SetResult(interp, result_list);
+
+       return JIM_OK;
+}
+
+static int jim_read_memory(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
+{
+       struct command_context *context;
+
+       context = current_command_context(interp);
+       assert(context != NULL);
+
+       struct target *target = get_current_target(context);
+       if (!target) {
+               LOG_ERROR("read_memory: no current target");
+               return JIM_ERR;
+       }
+
+       return target_jim_read_memory(interp, target, argc - 1, argv + 1);
+}
+
 static int get_u64_array_element(Jim_Interp *interp, const char *varname, 
size_t idx, uint64_t *val)
 {
        char *namebuf = alloc_printf("%s(%u)", varname, (unsigned int)idx);
@@ -4764,6 +4937,168 @@ static int target_array2mem(Jim_Interp *interp, struct 
target *target,
        return e;
 }
 
+static int target_jim_write_memory(Jim_Interp *interp, struct target *target,
+               int argc, Jim_Obj *const *argv)
+{
+       /*
+        * argv[0] = memory address
+        * argv[1] = desired element width in bits
+        * argv[2] = number of elements to write
+        * argv[3] = optional "phys"
+        */
+
+       if (argc < 3 || argc > 4) {
+               Jim_WrongNumArgs(interp, 0, argv, "address width data [phys]");
+               return JIM_ERR;
+       }
+
+       /* Arg 0: Memory address. */
+       int e;
+       jim_wide wide_addr;
+       e = Jim_GetWide(interp, argv[0], &wide_addr);
+
+       if (e != JIM_OK)
+               return e;
+
+       target_addr_t addr = (target_addr_t)wide_addr;
+
+       /* Arg 1: Bit width of one element. */
+       long l;
+       e = Jim_GetLong(interp, argv[1], &l);
+
+       if (e != JIM_OK)
+               return e;
+
+       const unsigned int width_bits = l;
+       size_t count = Jim_ListLength(interp, argv[2]);
+
+       /* Arg 3: Optional 'phys'. */
+       bool is_phys = false;
+
+       if (argc > 3) {
+               int str_len;
+               const char *phys = Jim_GetString(argv[3], &str_len);
+               if (!strncmp(phys, "phys", str_len))
+                       is_phys = true;
+               else
+                       return JIM_ERR;
+       }
+
+       switch (width_bits) {
+       case 8:
+       case 16:
+       case 32:
+       case 64:
+               break;
+       default:
+               Jim_SetResultString(interp, "Invalid width param, must be 
8/16/32", -1);
+               return JIM_ERR;
+       }
+
+       const unsigned int width = width_bits / 8;
+
+       if ((addr + (count * width)) < addr) {
+               Jim_SetResultString(interp, "write_memory: addr + len wraps to 
zero", -1);
+               return JIM_ERR;
+       }
+
+       if (count > 65536) {
+               Jim_SetResultString(interp, "write_memory: too large memory 
write request, exceeds 64K elements", -1);
+               return JIM_ERR;
+       }
+
+       if ((width == 1) ||
+               ((width == 2) && ((addr & 1) == 0)) ||
+               ((width == 4) && ((addr & 3) == 0)) ||
+               ((width == 8) && ((addr & 7) == 0))) {
+               /* Alignment is correct. */
+       } else {
+               char buf[128];
+               Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+               snprintf(buf, sizeof(buf), "write_memory: " TARGET_ADDR_FMT " 
is not aligned for %u byte writes",
+                       addr, width);
+               Jim_AppendStrings(interp, Jim_GetResult(interp), buf, NULL);
+               return JIM_ERR;
+       }
+
+       const size_t buffersize = 4096;
+       uint8_t *buffer = malloc(buffersize);
+
+       if (buffer == NULL)
+               return JIM_ERR;
+
+       Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+
+       size_t j = 0;
+
+       while (count > 0) {
+               const unsigned int max_chunk_len = buffersize / width;
+               const size_t chunk_len = MIN(count, max_chunk_len);
+
+               for (size_t i = 0; i < chunk_len; i++, j++) {
+                       Jim_Obj *tmp = Jim_ListGetIndex(interp, argv[2], j);
+                       jim_wide element_wide;
+                       Jim_GetWide(interp, tmp, &element_wide);
+
+                       const uint64_t v = element_wide;
+
+                       switch (width) {
+                       case 8:
+                               target_buffer_set_u64(target, &buffer[i * 
width], v);
+                               break;
+                       case 4:
+                               target_buffer_set_u32(target, &buffer[i * 
width], v);
+                               break;
+                       case 2:
+                               target_buffer_set_u16(target, &buffer[i * 
width], v);
+                               break;
+                       case 1:
+                               buffer[i] = v & 0x0ff;
+                               break;
+                       }
+               }
+
+               count -= chunk_len;
+
+               int retval;
+
+               if (is_phys)
+                       retval = target_write_phys_memory(target, addr, width, 
chunk_len, buffer);
+               else
+                       retval = target_write_memory(target, addr, width, 
chunk_len, buffer);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("write_memory: write at " TARGET_ADDR_FMT " 
with width=%u and count=%zu failed",
+                               addr,  width_bits, chunk_len);
+                       Jim_SetResultString(interp, "write_memory: failed to 
write memory", -1);
+                       e = JIM_ERR;
+                       break;
+               }
+
+               addr += chunk_len * width;
+       }
+
+       free(buffer);
+
+       return e;
+}
+
+static int jim_write_memory(Jim_Interp *interp, int argc, Jim_Obj * const 
*argv)
+{
+       struct command_context *context;
+
+       context = current_command_context(interp);
+       assert(context != NULL);
+
+       struct target *target = get_current_target(context);
+       if (!target) {
+               LOG_ERROR("write_memory: no current target");
+               return JIM_ERR;
+       }
+
+       return target_jim_write_memory(interp, target, argc - 1, argv + 1);
+}
+
+
 /* FIX? should we propagate errors here rather than printing them
  * and continuing?
  */
@@ -5235,6 +5570,24 @@ static int jim_target_array2mem(Jim_Interp *interp,
        return target_array2mem(interp, target, argc - 1, argv + 1);
 }
 
+static int jim_target_read_memory(Jim_Interp *interp, int argc,
+               Jim_Obj * const *argv)
+{
+       struct command_context *cmd_ctx = current_command_context(interp);
+       assert(cmd_ctx);
+       return target_jim_read_memory(interp, get_current_target(cmd_ctx), argc 
- 1,
+               argv + 1);
+}
+
+static int jim_target_write_memory(Jim_Interp *interp, int argc,
+               Jim_Obj * const *argv)
+{
+       struct command_context *cmd_ctx = current_command_context(interp);
+       assert(cmd_ctx);
+       return target_jim_write_memory(interp, get_current_target(cmd_ctx),
+               argc - 1, argv + 1);
+}
+
 static int jim_target_tap_disabled(Jim_Interp *interp)
 {
        Jim_SetResultFormatted(interp, "[TAP is disabled]");
@@ -5591,6 +5944,20 @@ static const struct command_registration 
target_instance_command_handlers[] = {
                .usage = "arrayname bitwidth address count",
        },
        {
+               .name = "read_memory",
+               .mode = COMMAND_EXEC,
+               .jim_handler = jim_target_read_memory,
+               .help = "Load Tcl list of 8/16/32/64 bit numbers from target 
memory",
+               .usage = "address width count ['phys']",
+       },
+       {
+               .name = "write_memory",
+               .mode = COMMAND_EXEC,
+               .jim_handler = jim_target_write_memory,
+               .help = "Write Tcl list of 8/16/32/64 bit numbers to target 
memory",
+               .usage = "address width data ['phys']",
+       },
+       {
                .name = "eventlist",
                .handler = handle_target_event_list,
                .mode = COMMAND_EXEC,
@@ -6677,6 +7044,20 @@ static const struct command_registration 
target_exec_command_handlers[] = {
                .usage = "arrayname bitwidth address count",
        },
        {
+               .name = "read_memory",
+               .mode = COMMAND_EXEC,
+               .jim_handler = jim_read_memory,
+               .help = "Load Tcl list of 8/16/32/64 bit numbers from target 
memory",
+               .usage = "address width count ['phys']",
+       },
+       {
+               .name = "write_memory",
+               .mode = COMMAND_EXEC,
+               .jim_handler = jim_write_memory,
+               .help = "Write Tcl list of 8/16/32/64 bit numbers to target 
memory",
+               .usage = "address width data ['phys']",
+       },
+       {
                .name = "reset_nag",
                .handler = handle_target_reset_nag,
                .mode = COMMAND_ANY,

-- 

Reply via email to