target armv7m: multi-block erase check 98/4298/5
authorTomas Vanek <vanekt@fbl.cz>
Thu, 23 Nov 2017 10:17:53 +0000 (11:17 +0100)
committerTomas Vanek <vanekt@fbl.cz>
Tue, 10 Apr 2018 05:17:00 +0000 (06:17 +0100)
Tested on PSoC6 (Cortex-M0+ core), onboard KitProg2 in CMSIS-DAP mode,
adapter_khz=1000.
Plain read:
flash read_bank 0 /dev/null
takes 48 seconds.

erase_check without this change:
flash erase_check 0
takes horrible 149 seconds!!

And the same command with the change applied takes 1.8 seconds.
Quite a difference.

Remove the erase-value=0 version of algorithm as the new one can check
for any value.

If the target is an insane slow clocked CPU (under 1MHz) algo
timeouts. Blocks checked so far are returned and the next call
uses increased timeout.

Change-Id: Ic0899011256d2114112e67c0b51fab4f6230d9cd
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4298
Tested-by: jenkins
Reviewed-by: Jonas Norling <jonas.norling@cyanconnode.com>
Reviewed-by: Andreas Bolsch <hyphen0break@gmail.com>
contrib/loaders/erase_check/Makefile
contrib/loaders/erase_check/armv7m_0_erase_check.inc [deleted file]
contrib/loaders/erase_check/armv7m_0_erase_check.s [deleted file]
contrib/loaders/erase_check/armv7m_erase_check.inc
contrib/loaders/erase_check/armv7m_erase_check.s
src/target/armv7m.c

index 427fa0c..1a0fd9e 100644 (file)
@@ -12,7 +12,7 @@ STM8_OBJCOPY ?= $(STM8_CROSS_COMPILE)objcopy
 
 STM8_AFLAGS =
 
-arm: armv4_5_erase_check.inc armv7m_erase_check.inc armv7m_0_erase_check.inc
+arm: armv4_5_erase_check.inc armv7m_erase_check.inc
 
 armv4_5_%.elf: armv4_5_%.s
        $(ARM_AS) $(ARM_AFLAGS) $< -o $@
diff --git a/contrib/loaders/erase_check/armv7m_0_erase_check.inc b/contrib/loaders/erase_check/armv7m_0_erase_check.inc
deleted file mode 100644 (file)
index 76115ec..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/* Autogenerated with ../../../src/helper/bin2char.sh */
-0x03,0x78,0x01,0x30,0x1a,0x43,0x01,0x39,0xfa,0xd1,0x00,0xbe,
diff --git a/contrib/loaders/erase_check/armv7m_0_erase_check.s b/contrib/loaders/erase_check/armv7m_0_erase_check.s
deleted file mode 100644 (file)
index 6b1e92a..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2014 by Jeff Ciesielski                                 *
- *   jeffciesielski@gmail.com                                              *
- *                                                                         *
- *   Based on the armv7m erase checker by:                                 *
- *   Copyright (C) 2010 by Spencer Oliver                                  *
- *   spen@spen-soft.co.uk                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License for more details.                          *
- *                                                                         *
- ***************************************************************************/
-
-/*
-       parameters:
-       r0 - address in
-       r1 - byte count
-       r2 - mask - result out
-*/
-
-       .text
-       .syntax unified
-       .cpu cortex-m0
-       .thumb
-       .thumb_func
-
-       .align  2
-
-loop:
-       ldrb    r3, [r0]
-       adds    r0, #1
-       orrs    r2, r2, r3
-       subs    r1, r1, #1
-       bne             loop
-end:
-       bkpt    #0
-
-       .end
index 1fe25cd..4ee96e1 100644 (file)
@@ -1,2 +1,4 @@
 /* Autogenerated with ../../../src/helper/bin2char.sh */
-0x03,0x78,0x01,0x30,0x1a,0x40,0x01,0x39,0xfa,0xd1,0x00,0xbe,
+0x02,0x68,0x12,0x42,0x0d,0xd0,0x43,0x68,0x1c,0x68,0x04,0x33,0x8c,0x42,0x05,0xd1,
+0x01,0x3a,0xf9,0xd1,0x01,0x24,0x04,0x60,0x08,0x30,0xf1,0xe7,0x00,0x24,0xfa,0xe7,
+0x00,0x00,0x00,0xbe,
index 886e3e2..3303c87 100644 (file)
@@ -20,9 +20,8 @@
 
 /*
        parameters:
-       r0 - address in
-       r1 - byte count
-       r2 - mask - result out
+       r0 - pointer to struct { uint32_t size_in_result_out, uint32_t addr }
+       r1 - value to check
 */
 
        .text
 
        .align  2
 
-loop:
-       ldrb    r3, [r0]
-       adds    r0, #1
-       ands    r2, r2, r3
-       subs    r1, r1, #1
-       bne             loop
-end:
+BLOCK_SIZE_RESULT      = 0
+BLOCK_ADDRESS          = 4
+SIZEOF_STRUCT_BLOCK    = 8
+
+start:
+block_loop:
+       ldr     r2, [r0, #BLOCK_SIZE_RESULT]    /* get size */
+       tst     r2, r2
+       beq     done
+
+       ldr     r3, [r0, #BLOCK_ADDRESS]        /* get address */
+
+word_loop:
+       ldr     r4, [r3]        /* read word */
+       adds    r3, #4
+
+       cmp     r4, r1
+       bne     not_erased
+
+       subs    r2, #1
+       bne     word_loop
+
+       movs    r4, #1          /* block is erased */
+save_result:
+       str     r4, [r0, #BLOCK_SIZE_RESULT]
+       adds    r0, #SIZEOF_STRUCT_BLOCK
+       b       block_loop
+
+not_erased:
+       movs    r4, #0
+       b       save_result
+
+/* Avoid padding at .text segment end. Otherwise exit point check fails. */
+        .skip   ( . - start + 2) & 2, 0
+
+done:
        bkpt    #0
 
        .end
index 696f85c..7b7893f 100644 (file)
@@ -731,34 +731,23 @@ cleanup:
        return retval;
 }
 
-/** Checks whether a memory region is erased. */
+/** Checks an array of memory regions whether they are erased. */
 int armv7m_blank_check_memory(struct target *target,
        struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value)
 {
        struct working_area *erase_check_algorithm;
-       struct reg_param reg_params[3];
+       struct working_area *erase_check_params;
+       struct reg_param reg_params[2];
        struct armv7m_algorithm armv7m_info;
-       const uint8_t *code;
-       uint32_t code_size;
        int retval;
 
+       static bool timed_out;
+
        static const uint8_t erase_check_code[] = {
 #include "../../contrib/loaders/erase_check/armv7m_erase_check.inc"
        };
-       static const uint8_t zero_erase_check_code[] = {
-#include "../../contrib/loaders/erase_check/armv7m_0_erase_check.inc"
-       };
 
-       switch (erased_value) {
-       case 0x00:
-               code = zero_erase_check_code;
-               code_size = sizeof(zero_erase_check_code);
-               break;
-       case 0xff:
-       default:
-               code = erase_check_code;
-               code_size = sizeof(erase_check_code);
-       }
+       const uint32_t code_size = sizeof(erase_check_code);
 
        /* make sure we have a working area */
        if (target_alloc_working_area(target, code_size,
@@ -766,46 +755,113 @@ int armv7m_blank_check_memory(struct target *target,
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
 
        retval = target_write_buffer(target, erase_check_algorithm->address,
-                       code_size, code);
+                       code_size, erase_check_code);
        if (retval != ERROR_OK)
-               goto cleanup;
+               goto cleanup1;
+
+       /* prepare blocks array for algo */
+       struct algo_block {
+               union {
+                       uint32_t size;
+                       uint32_t result;
+               };
+               uint32_t address;
+       };
+
+       uint32_t avail = target_get_working_area_avail(target);
+       int blocks_to_check = avail / sizeof(struct algo_block) - 1;
+       if (num_blocks < blocks_to_check)
+               blocks_to_check = num_blocks;
+
+       struct algo_block *params = malloc((blocks_to_check+1)*sizeof(struct algo_block));
+       if (params == NULL) {
+               retval = ERROR_FAIL;
+               goto cleanup1;
+       }
+
+       int i;
+       uint32_t total_size = 0;
+       for (i = 0; i < blocks_to_check; i++) {
+               total_size += blocks[i].size;
+               target_buffer_set_u32(target, (uint8_t *)&(params[i].size),
+                                               blocks[i].size / sizeof(uint32_t));
+               target_buffer_set_u32(target, (uint8_t *)&(params[i].address),
+                                               blocks[i].address);
+       }
+       target_buffer_set_u32(target, (uint8_t *)&(params[blocks_to_check].size), 0);
+
+       uint32_t param_size = (blocks_to_check + 1) * sizeof(struct algo_block);
+       if (target_alloc_working_area(target, param_size,
+                       &erase_check_params) != ERROR_OK) {
+               retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               goto cleanup2;
+       }
+
+       retval = target_write_buffer(target, erase_check_params->address,
+                               param_size, (uint8_t *)params);
+       if (retval != ERROR_OK)
+               goto cleanup3;
+
+       uint32_t erased_word = erased_value | (erased_value << 8)
+                              | (erased_value << 16) | (erased_value << 24);
+
+       LOG_DEBUG("Starting erase check of %d blocks, parameters@"
+                TARGET_ADDR_FMT, blocks_to_check, erase_check_params->address);
 
        armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
        armv7m_info.core_mode = ARM_MODE_THREAD;
 
        init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
-       buf_set_u32(reg_params[0].value, 0, 32, blocks->address);
+       buf_set_u32(reg_params[0].value, 0, 32, erase_check_params->address);
 
        init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
-       buf_set_u32(reg_params[1].value, 0, 32, blocks->size);
+       buf_set_u32(reg_params[1].value, 0, 32, erased_word);
 
-       init_reg_param(&reg_params[2], "r2", 32, PARAM_IN_OUT);
-       buf_set_u32(reg_params[2].value, 0, 32, erased_value);
+       /* assume CPU clk at least 1 MHz */
+       int timeout = (timed_out ? 30000 : 2000) + total_size * 3 / 1000;
 
        retval = target_run_algorithm(target,
-                       0,
-                       NULL,
-                       3,
-                       reg_params,
-                       erase_check_algorithm->address,
-                       erase_check_algorithm->address + (code_size - 2),
-                       10000,
-                       &armv7m_info);
+                               0, NULL,
+                               ARRAY_SIZE(reg_params), reg_params,
+                               erase_check_algorithm->address,
+                               erase_check_algorithm->address + (code_size - 2),
+                               timeout,
+                               &armv7m_info);
+
+       timed_out = retval == ERROR_TARGET_TIMEOUT;
+       if (retval != ERROR_OK && !timed_out)
+               goto cleanup4;
+
+       retval = target_read_buffer(target, erase_check_params->address,
+                               param_size, (uint8_t *)params);
+       if (retval != ERROR_OK)
+               goto cleanup4;
 
-       if (retval == ERROR_OK)
-               blocks->result = buf_get_u32(reg_params[2].value, 0, 32);
+       for (i = 0; i < blocks_to_check; i++) {
+               uint32_t result = target_buffer_get_u32(target,
+                                       (uint8_t *)&(params[i].result));
+               if (result != 0 && result != 1)
+                       break;
+
+               blocks[i].result = result;
+       }
+       if (i && timed_out)
+               LOG_INFO("Slow CPU clock: %d blocks checked, %d remain. Continuing...", i, num_blocks-i);
+
+       retval = i;             /* return number of blocks really checked */
 
+cleanup4:
        destroy_reg_param(&reg_params[0]);
        destroy_reg_param(&reg_params[1]);
-       destroy_reg_param(&reg_params[2]);
 
-cleanup:
+cleanup3:
+       target_free_working_area(target, erase_check_params);
+cleanup2:
+       free(params);
+cleanup1:
        target_free_working_area(target, erase_check_algorithm);
 
-       if (retval != ERROR_OK)
-               return retval;
-
-       return 1;       /* only one block checked */
+       return retval;
 }
 
 int armv7m_maybe_skip_bkpt_inst(struct target *target, bool *inst_found)