flash/nor/stm32f1x: unify flash error reporting
[openocd.git] / src / flash / nor / stm32f1x.c
index d5d59c6113731a8d4ef9f540962e787d69c5e766..26231e9cbbab789cda33438cfc533e20b5759dee 100644 (file)
@@ -29,7 +29,7 @@
 #include "imp.h"
 #include <helper/binarybuffer.h>
 #include <target/algorithm.h>
-#include <target/armv7m.h>
+#include <target/cortex_m.h>
 
 /* stm32x register locations */
 
@@ -116,7 +116,7 @@ struct stm32x_options {
 struct stm32x_flash_bank {
        struct stm32x_options option_bytes;
        int ppage_size;
-       int probed;
+       bool probed;
 
        bool has_dual_banks;
        /* used to access dual flash bank stm32xl */
@@ -131,7 +131,7 @@ struct stm32x_flash_bank {
 static int stm32x_mass_erase(struct flash_bank *bank);
 static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id);
 static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
-               uint32_t address, uint32_t count);
+               uint32_t address, uint32_t hwords_count);
 
 /* flash bank stm32x <base> <size> 0 0 <target#>
  */
@@ -145,12 +145,15 @@ FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command)
        stm32x_info = malloc(sizeof(struct stm32x_flash_bank));
 
        bank->driver_priv = stm32x_info;
-       stm32x_info->probed = 0;
+       stm32x_info->probed = false;
        stm32x_info->has_dual_banks = false;
        stm32x_info->can_load_options = false;
        stm32x_info->register_base = FLASH_REG_BASE_B0;
        stm32x_info->user_bank_size = bank->size;
 
+       /* The flash write must be aligned to a halfword boundary */
+       bank->write_start_alignment = bank->write_end_alignment = 2;
+
        return ERROR_OK;
 }
 
@@ -182,19 +185,19 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout)
                        break;
                if (timeout-- <= 0) {
                        LOG_ERROR("timed out waiting for flash");
-                       return ERROR_FAIL;
+                       return ERROR_FLASH_BUSY;
                }
                alive_sleep(1);
        }
 
        if (status & FLASH_WRPRTERR) {
                LOG_ERROR("stm32x device protected");
-               retval = ERROR_FAIL;
+               retval = ERROR_FLASH_PROTECTED;
        }
 
        if (status & FLASH_PGERR) {
-               LOG_ERROR("stm32x device programming failed");
-               retval = ERROR_FAIL;
+               LOG_ERROR("stm32x device programming failed / flash not erased");
+               retval = ERROR_FLASH_OPERATION_FAILED;
        }
 
        /* Clear but report errors */
@@ -215,7 +218,7 @@ static int stm32x_check_operation_supported(struct flash_bank *bank)
        /* if we have a dual flash bank device then
         * we need to perform option byte stuff on bank0 only */
        if (stm32x_info->register_base != FLASH_REG_BASE_B0) {
-               LOG_ERROR("Option Byte Operation's must use bank0");
+               LOG_ERROR("Option byte operations must use bank 0");
                return ERROR_FLASH_OPERATION_FAILED;
        }
 
@@ -229,34 +232,20 @@ static int stm32x_read_options(struct flash_bank *bank)
        uint32_t option_bytes;
        int retval;
 
-       /* read user and read protection option bytes */
-       retval = target_read_u32(target, STM32_OB_RDP, &option_bytes);
+       /* read user and read protection option bytes, user data option bytes */
+       retval = target_read_u32(target, STM32_FLASH_OBR_B0, &option_bytes);
        if (retval != ERROR_OK)
                return retval;
 
-       stm32x_info->option_bytes.rdp = option_bytes & 0xFF;
-       stm32x_info->option_bytes.user = (option_bytes >> 16) & 0xFF;
-
-       /* read user data option bytes */
-       retval = target_read_u32(target, STM32_OB_DATA0, &option_bytes);
-       if (retval != ERROR_OK)
-               return retval;
-
-       stm32x_info->option_bytes.data = ((option_bytes >> 8) & 0xFF00) | (option_bytes & 0xFF);
+       stm32x_info->option_bytes.rdp = (option_bytes & (1 << OPT_READOUT)) ? 0 : stm32x_info->default_rdp;
+       stm32x_info->option_bytes.user = (option_bytes >> stm32x_info->option_offset >> 2) & 0xff;
+       stm32x_info->option_bytes.data = (option_bytes >> stm32x_info->user_data_offset) & 0xffff;
 
        /* read write protection option bytes */
-       retval = target_read_u32(target, STM32_OB_WRP0, &option_bytes);
+       retval = target_read_u32(target, STM32_FLASH_WRPR_B0, &stm32x_info->option_bytes.protection);
        if (retval != ERROR_OK)
                return retval;
 
-       stm32x_info->option_bytes.protection = ((option_bytes >> 8) & 0xFF00) | (option_bytes & 0xFF);
-
-       retval = target_read_u32(target, STM32_OB_WRP2, &option_bytes);
-       if (retval != ERROR_OK)
-               return retval;
-
-       stm32x_info->option_bytes.protection |= (((option_bytes >> 8) & 0xFF00) | (option_bytes & 0xFF)) << 16;
-
        return ERROR_OK;
 }
 
@@ -343,12 +332,14 @@ static int stm32x_write_options(struct flash_bank *bank)
        target_buffer_set_u16(target, opt_bytes + 12, (stm32x_info->option_bytes.protection >> 16) & 0xff);
        target_buffer_set_u16(target, opt_bytes + 14, (stm32x_info->option_bytes.protection >> 24) & 0xff);
 
+       /* Block write is preferred in favour of operation with ancient ST-Link
+        * firmwares without 16-bit memory access. See
+        * 480: flash: stm32f1x: write option bytes using the loader
+        * https://review.openocd.org/c/openocd/+/480
+        */
        retval = stm32x_write_block(bank, opt_bytes, STM32_OB_RDP, sizeof(opt_bytes) / 2);
-       if (retval != ERROR_OK) {
-               if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
-                       LOG_ERROR("working area required to erase options bytes");
+       if (retval != ERROR_OK)
                return retval;
-       }
 
        retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_LOCK);
        if (retval != ERROR_OK)
@@ -363,7 +354,7 @@ static int stm32x_protect_check(struct flash_bank *bank)
        uint32_t protection;
 
        int retval = stm32x_check_operation_supported(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        /* medium density - each bit refers to a 4 sector protection block
@@ -373,16 +364,16 @@ static int stm32x_protect_check(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       for (int i = 0; i < bank->num_prot_blocks; i++)
+       for (unsigned int i = 0; i < bank->num_prot_blocks; i++)
                bank->prot_blocks[i].is_protected = (protection & (1 << i)) ? 0 : 1;
 
        return ERROR_OK;
 }
 
-static int stm32x_erase(struct flash_bank *bank, int first, int last)
+static int stm32x_erase(struct flash_bank *bank, unsigned int first,
+               unsigned int last)
 {
        struct target *target = bank->target;
-       int i;
 
        if (bank->target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
@@ -400,7 +391,7 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
        if (retval != ERROR_OK)
                return retval;
 
-       for (i = first; i <= last; i++) {
+       for (unsigned int i = first; i <= last; i++) {
                retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PER);
                if (retval != ERROR_OK)
                        return retval;
@@ -416,8 +407,6 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
                retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
                if (retval != ERROR_OK)
                        return retval;
-
-               bank->sectors[i].is_erased = 1;
        }
 
        retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
@@ -427,7 +416,8 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
        return ERROR_OK;
 }
 
-static int stm32x_protect(struct flash_bank *bank, int set, int first, int last)
+static int stm32x_protect(struct flash_bank *bank, int set, unsigned int first,
+               unsigned int last)
 {
        struct target *target = bank->target;
        struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
@@ -447,7 +437,7 @@ static int stm32x_protect(struct flash_bank *bank, int set, int first, int last)
                return retval;
        }
 
-       for (int i = first; i <= last; i++) {
+       for (unsigned int i = first; i <= last; i++) {
                if (set)
                        stm32x_info->option_bytes.protection &= ~(1 << i);
                else
@@ -457,17 +447,16 @@ static int stm32x_protect(struct flash_bank *bank, int set, int first, int last)
        return stm32x_write_options(bank);
 }
 
-static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
-               uint32_t address, uint32_t count)
+static int stm32x_write_block_async(struct flash_bank *bank, const uint8_t *buffer,
+               uint32_t address, uint32_t hwords_count)
 {
        struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
        struct target *target = bank->target;
-       uint32_t buffer_size = 16384;
+       uint32_t buffer_size;
        struct working_area *write_algorithm;
        struct working_area *source;
-       struct reg_param reg_params[5];
        struct armv7m_algorithm armv7m_info;
-       int retval = ERROR_OK;
+       int retval;
 
        static const uint8_t stm32x_flash_write_code[] = {
 #include "../../../contrib/loaders/flash/stm32/stm32f1x.inc"
@@ -488,19 +477,28 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
        }
 
        /* memory buffer */
-       while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
-               buffer_size /= 2;
-               buffer_size &= ~3UL; /* Make sure it's 4 byte aligned */
-               if (buffer_size <= 256) {
-                       /* we already allocated the writing code, but failed to get a
-                        * buffer, free the algorithm */
-                       target_free_working_area(target, write_algorithm);
-
-                       LOG_WARNING("no large enough working area available, can't do block memory writes");
-                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
-               }
+       buffer_size = target_get_working_area_avail(target);
+       buffer_size = MIN(hwords_count * 2, MAX(buffer_size, 256));
+       /* Normally we allocate all available working area.
+        * MIN shrinks buffer_size if the size of the written block is smaller.
+        * MAX prevents using async algo if the available working area is smaller
+        * than 256, the following allocation fails with
+        * ERROR_TARGET_RESOURCE_NOT_AVAILABLE and slow flashing takes place.
+        */
+
+       retval = target_alloc_working_area(target, buffer_size, &source);
+       /* Allocated size is always 32-bit word aligned */
+       if (retval != ERROR_OK) {
+               target_free_working_area(target, write_algorithm);
+               LOG_WARNING("no large enough working area available, can't do block memory writes");
+               /* target_alloc_working_area() may return ERROR_FAIL if area backup fails:
+                * convert any error to ERROR_TARGET_RESOURCE_NOT_AVAILABLE
+                */
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
+       struct reg_param reg_params[5];
+
        init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT); /* flash base (in), status (out) */
        init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);    /* count (halfword-16bit) */
        init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);    /* buffer start */
@@ -508,7 +506,7 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
        init_reg_param(&reg_params[4], "r4", 32, PARAM_IN_OUT); /* target address */
 
        buf_set_u32(reg_params[0].value, 0, 32, stm32x_info->register_base);
-       buf_set_u32(reg_params[1].value, 0, 32, count);
+       buf_set_u32(reg_params[1].value, 0, 32, hwords_count);
        buf_set_u32(reg_params[2].value, 0, 32, source->address);
        buf_set_u32(reg_params[3].value, 0, 32, source->address + source->size);
        buf_set_u32(reg_params[4].value, 0, 32, address);
@@ -516,39 +514,73 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
        armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
        armv7m_info.core_mode = ARM_MODE_THREAD;
 
-       retval = target_run_flash_async_algorithm(target, buffer, count, 2,
+       retval = target_run_flash_async_algorithm(target, buffer, hwords_count, 2,
                        0, NULL,
-                       5, reg_params,
+                       ARRAY_SIZE(reg_params), reg_params,
                        source->address, source->size,
                        write_algorithm->address, 0,
                        &armv7m_info);
 
        if (retval == ERROR_FLASH_OPERATION_FAILED) {
-               LOG_ERROR("flash write failed at address 0x%"PRIx32,
-                               buf_get_u32(reg_params[4].value, 0, 32));
-
-               if (buf_get_u32(reg_params[0].value, 0, 32) & FLASH_PGERR) {
-                       LOG_ERROR("flash memory not erased before writing");
-                       /* Clear but report errors */
-                       target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_SR), FLASH_PGERR);
-               }
+               /* Actually we just need to check for programming errors
+                * stm32x_wait_status_busy also reports error and clears status bits.
+                *
+                * Target algo returns flash status in r0 only if properly finished.
+                * It is safer to re-read status register.
+                */
+               int retval2 = stm32x_wait_status_busy(bank, 5);
+               if (retval2 != ERROR_OK)
+                       retval = retval2;
 
-               if (buf_get_u32(reg_params[0].value, 0, 32) & FLASH_WRPRTERR) {
-                       LOG_ERROR("flash memory write protected");
-                       /* Clear but report errors */
-                       target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_SR), FLASH_WRPRTERR);
-               }
+               LOG_ERROR("flash write failed just before address 0x%"PRIx32,
+                               buf_get_u32(reg_params[4].value, 0, 32));
        }
 
+       for (unsigned int i = 0; i < ARRAY_SIZE(reg_params); i++)
+               destroy_reg_param(&reg_params[i]);
+
        target_free_working_area(target, source);
        target_free_working_area(target, write_algorithm);
 
-       destroy_reg_param(&reg_params[0]);
-       destroy_reg_param(&reg_params[1]);
-       destroy_reg_param(&reg_params[2]);
-       destroy_reg_param(&reg_params[3]);
-       destroy_reg_param(&reg_params[4]);
+       return retval;
+}
+
+/** Writes a block to flash either using target algorithm
+ *  or use fallback, host controlled halfword-by-halfword access.
+ *  Flash controller must be unlocked before this call.
+ */
+static int stm32x_write_block(struct flash_bank *bank,
+               const uint8_t *buffer, uint32_t address, uint32_t hwords_count)
+{
+       struct target *target = bank->target;
+
+       /* The flash write must be aligned to a halfword boundary.
+        * The flash infrastructure ensures it, do just a security check
+        */
+       assert(address % 2 == 0);
+
+       /* try using a block write - on ARM architecture or... */
+       int retval = stm32x_write_block_async(bank, buffer, address, hwords_count);
+
+       if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
+               /* if block write failed (no sufficient working area),
+                * we use normal (slow) single halfword accesses */
+               LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
 
+               while (hwords_count > 0) {
+                       retval = target_write_memory(target, address, 2, 1, buffer);
+                       if (retval != ERROR_OK)
+                               return retval;
+
+                       retval = stm32x_wait_status_busy(bank, 5);
+                       if (retval != ERROR_OK)
+                               return retval;
+
+                       hwords_count--;
+                       buffer += 2;
+                       address += 2;
+               }
+       }
        return retval;
 }
 
@@ -556,116 +588,74 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer,
                uint32_t offset, uint32_t count)
 {
        struct target *target = bank->target;
-       uint8_t *new_buffer = NULL;
 
        if (bank->target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       if (offset & 0x1) {
-               LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
-               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
-       }
+       /* The flash write must be aligned to a halfword boundary.
+        * The flash infrastructure ensures it, do just a security check
+        */
+       assert(offset % 2 == 0);
+       assert(count % 2 == 0);
 
-       /* If there's an odd number of bytes, the data has to be padded. Duplicate
-        * the buffer and use the normal code path with a single block write since
-        * it's probably cheaper than to special case the last odd write using
-        * discrete accesses. */
-       if (count & 1) {
-               new_buffer = malloc(count + 1);
-               if (new_buffer == NULL) {
-                       LOG_ERROR("odd number of bytes to write and no memory for padding buffer");
-                       return ERROR_FAIL;
-               }
-               LOG_INFO("odd number of bytes to write, padding with 0xff");
-               buffer = memcpy(new_buffer, buffer, count);
-               new_buffer[count++] = 0xff;
-       }
-
-       uint32_t words_remaining = count / 2;
        int retval, retval2;
 
        /* unlock flash registers */
        retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY1);
        if (retval != ERROR_OK)
-               goto cleanup;
+               return retval;
        retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2);
        if (retval != ERROR_OK)
-               goto cleanup;
+               goto reset_pg_and_lock;
 
+       /* enable flash programming */
        retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PG);
        if (retval != ERROR_OK)
-               goto cleanup;
-
-       /* try using a block write */
-       retval = stm32x_write_block(bank, buffer, bank->base + offset, words_remaining);
-
-       if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
-               /* if block write failed (no sufficient working area),
-                * we use normal (slow) single halfword accesses */
-               LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
+               goto reset_pg_and_lock;
 
-               while (words_remaining > 0) {
-                       uint16_t value;
-                       memcpy(&value, buffer, sizeof(uint16_t));
-
-                       retval = target_write_u16(target, bank->base + offset, value);
-                       if (retval != ERROR_OK)
-                               goto reset_pg_and_lock;
-
-                       retval = stm32x_wait_status_busy(bank, 5);
-                       if (retval != ERROR_OK)
-                               goto reset_pg_and_lock;
-
-                       words_remaining--;
-                       buffer += 2;
-                       offset += 2;
-               }
-       }
+       /* write to flash */
+       retval = stm32x_write_block(bank, buffer, bank->base + offset, count / 2);
 
 reset_pg_and_lock:
        retval2 = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK);
        if (retval == ERROR_OK)
                retval = retval2;
 
-cleanup:
-       if (new_buffer)
-               free(new_buffer);
-
        return retval;
 }
 
 static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id)
 {
-       /* This check the device CPUID core register to detect
-        * the M0 from the M3 devices. */
-
        struct target *target = bank->target;
-       uint32_t cpuid, device_id_register = 0;
+       uint32_t device_id_register = 0;
 
-       /* Get the CPUID from the ARM Core
-        * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0432c/DDI0432C_cortex_m0_r0p0_trm.pdf 4.2.1 */
-       int retval = target_read_u32(target, 0xE000ED00, &cpuid);
-       if (retval != ERROR_OK)
-               return retval;
+       if (!target_was_examined(target)) {
+               LOG_ERROR("Target not examined yet");
+               return ERROR_TARGET_NOT_EXAMINED;
+       }
 
-       if (((cpuid >> 4) & 0xFFF) == 0xC20) {
-               /* 0xC20 is M0 devices */
+       switch (cortex_m_get_partno_safe(target)) {
+       case CORTEX_M0_PARTNO: /* STM32F0x devices */
                device_id_register = 0x40015800;
-       } else if (((cpuid >> 4) & 0xFFF) == 0xC23) {
-               /* 0xC23 is M3 devices */
+               break;
+       case CORTEX_M3_PARTNO: /* STM32F1x devices */
                device_id_register = 0xE0042000;
-       } else if (((cpuid >> 4) & 0xFFF) == 0xC24) {
-               /* 0xC24 is M4 devices */
+               break;
+       case CORTEX_M4_PARTNO: /* STM32F3x devices */
                device_id_register = 0xE0042000;
-       } else {
+               break;
+       case CORTEX_M23_PARTNO: /* GD32E23x devices */
+               device_id_register = 0x40015800;
+               break;
+       default:
                LOG_ERROR("Cannot identify target as a stm32x");
                return ERROR_FAIL;
        }
 
        /* read stm32 device id register */
-       retval = target_read_u32(target, device_id_register, device_id);
+       int retval = target_read_u32(target, device_id_register, device_id);
        if (retval != ERROR_OK)
                return retval;
 
@@ -675,27 +665,32 @@ static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id)
 static int stm32x_get_flash_size(struct flash_bank *bank, uint16_t *flash_size_in_kb)
 {
        struct target *target = bank->target;
-       uint32_t cpuid, flash_size_reg;
+       uint32_t flash_size_reg;
 
-       int retval = target_read_u32(target, 0xE000ED00, &cpuid);
-       if (retval != ERROR_OK)
-               return retval;
+       if (!target_was_examined(target)) {
+               LOG_ERROR("Target not examined yet");
+               return ERROR_TARGET_NOT_EXAMINED;
+       }
 
-       if (((cpuid >> 4) & 0xFFF) == 0xC20) {
-               /* 0xC20 is M0 devices */
+       switch (cortex_m_get_partno_safe(target)) {
+       case CORTEX_M0_PARTNO: /* STM32F0x devices */
                flash_size_reg = 0x1FFFF7CC;
-       } else if (((cpuid >> 4) & 0xFFF) == 0xC23) {
-               /* 0xC23 is M3 devices */
+               break;
+       case CORTEX_M3_PARTNO: /* STM32F1x devices */
                flash_size_reg = 0x1FFFF7E0;
-       } else if (((cpuid >> 4) & 0xFFF) == 0xC24) {
-               /* 0xC24 is M4 devices */
+               break;
+       case CORTEX_M4_PARTNO: /* STM32F3x devices */
                flash_size_reg = 0x1FFFF7CC;
-       } else {
+               break;
+       case CORTEX_M23_PARTNO: /* GD32E23x devices */
+               flash_size_reg = 0x1FFFF7E0;
+               break;
+       default:
                LOG_ERROR("Cannot identify target as a stm32x");
                return ERROR_FAIL;
        }
 
-       retval = target_read_u16(target, flash_size_reg, flash_size_in_kb);
+       int retval = target_read_u16(target, flash_size_reg, flash_size_in_kb);
        if (retval != ERROR_OK)
                return retval;
 
@@ -707,11 +702,11 @@ static int stm32x_probe(struct flash_bank *bank)
        struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
        uint16_t flash_size_in_kb;
        uint16_t max_flash_size_in_kb;
-       uint32_t device_id;
+       uint32_t dbgmcu_idcode;
        int page_size;
        uint32_t base_address = 0x08000000;
 
-       stm32x_info->probed = 0;
+       stm32x_info->probed = false;
        stm32x_info->register_base = FLASH_REG_BASE_B0;
        stm32x_info->user_data_offset = 10;
        stm32x_info->option_offset = 0;
@@ -720,39 +715,114 @@ static int stm32x_probe(struct flash_bank *bank)
        stm32x_info->default_rdp = 0xA5;
 
        /* read stm32 device id register */
-       int retval = stm32x_get_device_id(bank, &device_id);
+       int retval = stm32x_get_device_id(bank, &dbgmcu_idcode);
        if (retval != ERROR_OK)
                return retval;
 
-       LOG_INFO("device id = 0x%08" PRIx32 "", device_id);
+       LOG_INFO("device id = 0x%08" PRIx32 "", dbgmcu_idcode);
+
+       uint16_t device_id = dbgmcu_idcode & 0xfff;
+       uint16_t rev_id = dbgmcu_idcode >> 16;
 
        /* set page size, protection granularity and max flash size depending on family */
-       switch (device_id & 0xfff) {
-       case 0x410: /* medium density */
+       switch (device_id) {
+       case 0x440: /* stm32f05x */
+               page_size = 1024;
+               stm32x_info->ppage_size = 4;
+               max_flash_size_in_kb = 64;
+               stm32x_info->user_data_offset = 16;
+               stm32x_info->option_offset = 6;
+               stm32x_info->default_rdp = 0xAA;
+               stm32x_info->can_load_options = true;
+               break;
+       case 0x444: /* stm32f03x */
+       case 0x445: /* stm32f04x */
+               page_size = 1024;
+               stm32x_info->ppage_size = 4;
+               max_flash_size_in_kb = 32;
+               stm32x_info->user_data_offset = 16;
+               stm32x_info->option_offset = 6;
+               stm32x_info->default_rdp = 0xAA;
+               stm32x_info->can_load_options = true;
+               break;
+       case 0x448: /* stm32f07x */
+               page_size = 2048;
+               stm32x_info->ppage_size = 4;
+               max_flash_size_in_kb = 128;
+               stm32x_info->user_data_offset = 16;
+               stm32x_info->option_offset = 6;
+               stm32x_info->default_rdp = 0xAA;
+               stm32x_info->can_load_options = true;
+               break;
+       case 0x442: /* stm32f09x */
+               page_size = 2048;
+               stm32x_info->ppage_size = 4;
+               max_flash_size_in_kb = 256;
+               stm32x_info->user_data_offset = 16;
+               stm32x_info->option_offset = 6;
+               stm32x_info->default_rdp = 0xAA;
+               stm32x_info->can_load_options = true;
+               break;
+       case 0x410: /* stm32f1x medium-density */
                page_size = 1024;
                stm32x_info->ppage_size = 4;
                max_flash_size_in_kb = 128;
+               /* GigaDevice GD32F1x0 & GD32F3x0 & GD32E23x series devices
+                  share DEV_ID with STM32F101/2/3 medium-density line,
+                  however they use a REV_ID different from any STM32 device.
+                  The main difference is another offset of user option bits
+                  (like WDG_SW, nRST_STOP, nRST_STDBY) in option byte register
+                  (FLASH_OBR/FMC_OBSTAT 0x4002201C).
+                  This caused problems e.g. during flash block programming
+                  because of unexpected active hardware watchog. */
+               switch (rev_id) {
+               case 0x1303: /* gd32f1x0 */
+                       stm32x_info->user_data_offset = 16;
+                       stm32x_info->option_offset = 6;
+                       max_flash_size_in_kb = 64;
+                       break;
+               case 0x1704: /* gd32f3x0 */
+                       stm32x_info->user_data_offset = 16;
+                       stm32x_info->option_offset = 6;
+                       break;
+               case 0x1909: /* gd32e23x */
+                       stm32x_info->user_data_offset = 16;
+                       stm32x_info->option_offset = 6;
+                       max_flash_size_in_kb = 64;
+                       break;
+               }
                break;
-       case 0x412: /* low density */
+       case 0x412: /* stm32f1x low-density */
                page_size = 1024;
                stm32x_info->ppage_size = 4;
                max_flash_size_in_kb = 32;
                break;
-       case 0x414: /* high density */
+       case 0x414: /* stm32f1x high-density */
                page_size = 2048;
                stm32x_info->ppage_size = 2;
                max_flash_size_in_kb = 512;
                break;
-       case 0x418: /* connectivity line density */
+       case 0x418: /* stm32f1x connectivity */
                page_size = 2048;
                stm32x_info->ppage_size = 2;
                max_flash_size_in_kb = 256;
                break;
-       case 0x420: /* value line density */
+       case 0x430: /* stm32f1 XL-density (dual flash banks) */
+               page_size = 2048;
+               stm32x_info->ppage_size = 2;
+               max_flash_size_in_kb = 1024;
+               stm32x_info->has_dual_banks = true;
+               break;
+       case 0x420: /* stm32f100xx low- and medium-density value line */
                page_size = 1024;
                stm32x_info->ppage_size = 4;
                max_flash_size_in_kb = 128;
                break;
+       case 0x428: /* stm32f100xx high-density value line */
+               page_size = 2048;
+               stm32x_info->ppage_size = 4;
+               max_flash_size_in_kb = 512;
+               break;
        case 0x422: /* stm32f302/3xb/c */
                page_size = 2048;
                stm32x_info->ppage_size = 2;
@@ -771,17 +841,6 @@ static int stm32x_probe(struct flash_bank *bank)
                stm32x_info->default_rdp = 0xAA;
                stm32x_info->can_load_options = true;
                break;
-       case 0x428: /* value line High density */
-               page_size = 2048;
-               stm32x_info->ppage_size = 4;
-               max_flash_size_in_kb = 128;
-               break;
-       case 0x430: /* xl line density (dual flash banks) */
-               page_size = 2048;
-               stm32x_info->ppage_size = 2;
-               max_flash_size_in_kb = 1024;
-               stm32x_info->has_dual_banks = true;
-               break;
        case 0x432: /* stm32f37x */
                page_size = 2048;
                stm32x_info->ppage_size = 2;
@@ -801,27 +860,6 @@ static int stm32x_probe(struct flash_bank *bank)
                stm32x_info->default_rdp = 0xAA;
                stm32x_info->can_load_options = true;
                break;
-       case 0x440: /* stm32f05x */
-       case 0x444: /* stm32f03x */
-       case 0x445: /* stm32f04x */
-               page_size = 1024;
-               stm32x_info->ppage_size = 4;
-               max_flash_size_in_kb = 64;
-               stm32x_info->user_data_offset = 16;
-               stm32x_info->option_offset = 6;
-               stm32x_info->default_rdp = 0xAA;
-               stm32x_info->can_load_options = true;
-               break;
-       case 0x448: /* stm32f07x */
-       case 0x442: /* stm32f09x */
-               page_size = 2048;
-               stm32x_info->ppage_size = 4;
-               max_flash_size_in_kb = 256;
-               stm32x_info->user_data_offset = 16;
-               stm32x_info->option_offset = 6;
-               stm32x_info->default_rdp = 0xAA;
-               stm32x_info->can_load_options = true;
-               break;
        default:
                LOG_WARNING("Cannot identify target as a STM32 family.");
                return ERROR_FAIL;
@@ -869,15 +907,11 @@ static int stm32x_probe(struct flash_bank *bank)
        /* check that calculation result makes sense */
        assert(num_pages > 0);
 
-       if (bank->sectors) {
-               free(bank->sectors);
-               bank->sectors = NULL;
-       }
+       free(bank->sectors);
+       bank->sectors = NULL;
 
-       if (bank->prot_blocks) {
-               free(bank->prot_blocks);
-               bank->prot_blocks = NULL;
-       }
+       free(bank->prot_blocks);
+       bank->prot_blocks = NULL;
 
        bank->base = base_address;
        bank->size = (num_pages * page_size);
@@ -900,7 +934,7 @@ static int stm32x_probe(struct flash_bank *bank)
        if (num_prot_blocks == 32)
                bank->prot_blocks[31].size = (num_pages - (31 * stm32x_info->ppage_size)) * page_size;
 
-       stm32x_info->probed = 1;
+       stm32x_info->probed = true;
 
        return ERROR_OK;
 }
@@ -935,11 +969,11 @@ static const char *get_stm32f0_revision(uint16_t rev_id)
        return rev_str;
 }
 
-static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
+static int get_stm32x_info(struct flash_bank *bank, struct command_invocation *cmd)
 {
        uint32_t dbgmcu_idcode;
 
-               /* read stm32 device id register */
+       /* read stm32 device id register */
        int retval = stm32x_get_device_id(bank, &dbgmcu_idcode);
        if (retval != ERROR_OK)
                return retval;
@@ -958,6 +992,18 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
                        rev_str = "A";
                        break;
 
+               case 0x1303: /* gd32f1x0 */
+                       device_str = "GD32F1x0";
+                       break;
+
+               case 0x1704: /* gd32f3x0 */
+                       device_str = "GD32F3x0";
+                       break;
+
+               case 0x1909: /* gd32e23x */
+                       device_str = "GD32E23x";
+                       break;
+
                case 0x2000:
                        rev_str = "B";
                        break;
@@ -1147,14 +1193,14 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
                break;
 
        default:
-               snprintf(buf, buf_size, "Cannot identify target as a STM32F0/1/3\n");
+               command_print_sameline(cmd, "Cannot identify target as a STM32F0/1/3\n");
                return ERROR_FAIL;
        }
 
-       if (rev_str != NULL)
-               snprintf(buf, buf_size, "%s - Rev: %s", device_str, rev_str);
+       if (rev_str)
+               command_print_sameline(cmd, "%s - Rev: %s", device_str, rev_str);
        else
-               snprintf(buf, buf_size, "%s - Rev: unknown (0x%04x)", device_str, rev_id);
+               command_print_sameline(cmd, "%s - Rev: unknown (0x%04x)", device_str, rev_id);
 
        return ERROR_OK;
 }
@@ -1169,7 +1215,7 @@ COMMAND_HANDLER(stm32x_handle_lock_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        stm32x_info = bank->driver_priv;
@@ -1182,11 +1228,11 @@ COMMAND_HANDLER(stm32x_handle_lock_command)
        }
 
        retval = stm32x_check_operation_supported(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        if (stm32x_erase_options(bank) != ERROR_OK) {
-               command_print(CMD_CTX, "stm32x failed to erase options");
+               command_print(CMD, "stm32x failed to erase options");
                return ERROR_OK;
        }
 
@@ -1194,11 +1240,11 @@ COMMAND_HANDLER(stm32x_handle_lock_command)
        stm32x_info->option_bytes.rdp = 0;
 
        if (stm32x_write_options(bank) != ERROR_OK) {
-               command_print(CMD_CTX, "stm32x failed to lock device");
+               command_print(CMD, "stm32x failed to lock device");
                return ERROR_OK;
        }
 
-       command_print(CMD_CTX, "stm32x locked");
+       command_print(CMD, "stm32x locked");
 
        return ERROR_OK;
 }
@@ -1212,7 +1258,7 @@ COMMAND_HANDLER(stm32x_handle_unlock_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        target = bank->target;
@@ -1223,20 +1269,20 @@ COMMAND_HANDLER(stm32x_handle_unlock_command)
        }
 
        retval = stm32x_check_operation_supported(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        if (stm32x_erase_options(bank) != ERROR_OK) {
-               command_print(CMD_CTX, "stm32x failed to erase options");
+               command_print(CMD, "stm32x failed to erase options");
                return ERROR_OK;
        }
 
        if (stm32x_write_options(bank) != ERROR_OK) {
-               command_print(CMD_CTX, "stm32x failed to unlock device");
+               command_print(CMD, "stm32x failed to unlock device");
                return ERROR_OK;
        }
 
-       command_print(CMD_CTX, "stm32x unlocked.\n"
+       command_print(CMD, "stm32x unlocked.\n"
                        "INFO: a reset or power cycle is required "
                        "for the new settings to take effect.");
 
@@ -1254,7 +1300,7 @@ COMMAND_HANDLER(stm32x_handle_options_read_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        stm32x_info = bank->driver_priv;
@@ -1267,7 +1313,7 @@ COMMAND_HANDLER(stm32x_handle_options_read_command)
        }
 
        retval = stm32x_check_operation_supported(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        retval = target_read_u32(target, STM32_FLASH_OBR_B0, &optionbyte);
@@ -1281,30 +1327,30 @@ COMMAND_HANDLER(stm32x_handle_options_read_command)
                return retval;
 
        if (optionbyte & (1 << OPT_ERROR))
-               command_print(CMD_CTX, "option byte complement error");
+               command_print(CMD, "option byte complement error");
 
-       command_print(CMD_CTX, "option byte register = 0x%" PRIx32 "", optionbyte);
-       command_print(CMD_CTX, "write protection register = 0x%" PRIx32 "", protection);
+       command_print(CMD, "option byte register = 0x%" PRIx32 "", optionbyte);
+       command_print(CMD, "write protection register = 0x%" PRIx32 "", protection);
 
-       command_print(CMD_CTX, "read protection: %s",
+       command_print(CMD, "read protection: %s",
                                (optionbyte & (1 << OPT_READOUT)) ? "on" : "off");
 
        /* user option bytes are offset depending on variant */
        optionbyte >>= stm32x_info->option_offset;
 
-       command_print(CMD_CTX, "watchdog: %sware",
+       command_print(CMD, "watchdog: %sware",
                                (optionbyte & (1 << OPT_RDWDGSW)) ? "soft" : "hard");
 
-       command_print(CMD_CTX, "stop mode: %sreset generated upon entry",
+       command_print(CMD, "stop mode: %sreset generated upon entry",
                                (optionbyte & (1 << OPT_RDRSTSTOP)) ? "no " : "");
 
-       command_print(CMD_CTX, "standby mode: %sreset generated upon entry",
+       command_print(CMD, "standby mode: %sreset generated upon entry",
                                (optionbyte & (1 << OPT_RDRSTSTDBY)) ? "no " : "");
 
        if (stm32x_info->has_dual_banks)
-               command_print(CMD_CTX, "boot: bank %d", (optionbyte & (1 << OPT_BFB2)) ? 0 : 1);
+               command_print(CMD, "boot: bank %d", (optionbyte & (1 << OPT_BFB2)) ? 0 : 1);
 
-       command_print(CMD_CTX, "user data = 0x%02" PRIx16 "", user_data);
+       command_print(CMD, "user data = 0x%02" PRIx16 "", user_data);
 
        return ERROR_OK;
 }
@@ -1321,7 +1367,7 @@ COMMAND_HANDLER(stm32x_handle_options_write_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        stm32x_info = bank->driver_priv;
@@ -1334,11 +1380,11 @@ COMMAND_HANDLER(stm32x_handle_options_write_command)
        }
 
        retval = stm32x_check_operation_supported(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        retval = stm32x_read_options(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        /* start with current options */
@@ -1368,8 +1414,7 @@ COMMAND_HANDLER(stm32x_handle_options_write_command)
                        COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], useropt);
                        CMD_ARGC--;
                        CMD_ARGV++;
-               }
-               else if (stm32x_info->has_dual_banks) {
+               } else if (stm32x_info->has_dual_banks) {
                        if (strcmp("BOOT0", CMD_ARGV[0]) == 0)
                                optionbyte |= (1 << 3);
                        else if (strcmp("BOOT1", CMD_ARGV[0]) == 0)
@@ -1383,7 +1428,7 @@ COMMAND_HANDLER(stm32x_handle_options_write_command)
        }
 
        if (stm32x_erase_options(bank) != ERROR_OK) {
-               command_print(CMD_CTX, "stm32x failed to erase options");
+               command_print(CMD, "stm32x failed to erase options");
                return ERROR_OK;
        }
 
@@ -1391,11 +1436,11 @@ COMMAND_HANDLER(stm32x_handle_options_write_command)
        stm32x_info->option_bytes.data = useropt;
 
        if (stm32x_write_options(bank) != ERROR_OK) {
-               command_print(CMD_CTX, "stm32x failed to write options");
+               command_print(CMD, "stm32x failed to write options");
                return ERROR_OK;
        }
 
-       command_print(CMD_CTX, "stm32x write options complete.\n"
+       command_print(CMD, "stm32x write options complete.\n"
                                "INFO: %spower cycle is required "
                                "for the new settings to take effect.",
                                stm32x_info->can_load_options
@@ -1411,7 +1456,7 @@ COMMAND_HANDLER(stm32x_handle_options_load_command)
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
@@ -1430,7 +1475,7 @@ COMMAND_HANDLER(stm32x_handle_options_load_command)
        }
 
        retval = stm32x_check_operation_supported(bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        /* unlock option flash registers */
@@ -1488,30 +1533,24 @@ static int stm32x_mass_erase(struct flash_bank *bank)
 
 COMMAND_HANDLER(stm32x_handle_mass_erase_command)
 {
-       int i;
-
        if (CMD_ARGC < 1)
                return ERROR_COMMAND_SYNTAX_ERROR;
 
        struct flash_bank *bank;
        int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
-       if (ERROR_OK != retval)
+       if (retval != ERROR_OK)
                return retval;
 
        retval = stm32x_mass_erase(bank);
-       if (retval == ERROR_OK) {
-               /* set all sectors as erased */
-               for (i = 0; i < bank->num_sectors; i++)
-                       bank->sectors[i].is_erased = 1;
-
-               command_print(CMD_CTX, "stm32x mass erase complete");
-       } else
-               command_print(CMD_CTX, "stm32x mass erase failed");
+       if (retval == ERROR_OK)
+               command_print(CMD, "stm32x mass erase complete");
+       else
+               command_print(CMD, "stm32x mass erase failed");
 
        return retval;
 }
 
-static const struct command_registration stm32x_exec_command_handlers[] = {
+static const struct command_registration stm32f1x_exec_command_handlers[] = {
        {
                .name = "lock",
                .handler = stm32x_handle_lock_command,
@@ -1559,20 +1598,20 @@ static const struct command_registration stm32x_exec_command_handlers[] = {
        COMMAND_REGISTRATION_DONE
 };
 
-static const struct command_registration stm32x_command_handlers[] = {
+static const struct command_registration stm32f1x_command_handlers[] = {
        {
                .name = "stm32f1x",
                .mode = COMMAND_ANY,
                .help = "stm32f1x flash command group",
                .usage = "",
-               .chain = stm32x_exec_command_handlers,
+               .chain = stm32f1x_exec_command_handlers,
        },
        COMMAND_REGISTRATION_DONE
 };
 
 const struct flash_driver stm32f1x_flash = {
        .name = "stm32f1x",
-       .commands = stm32x_command_handlers,
+       .commands = stm32f1x_command_handlers,
        .flash_bank_command = stm32x_flash_bank_command,
        .erase = stm32x_erase,
        .protect = stm32x_protect,

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)