stm32f1x: use async algorithm in flash programming routine
[openocd.git] / src / flash / nor / stm32f1x.c
index 6bc4300144f6111a561c8349349bd925c40e7aa4..a0520c79278b3ee497bf42c36ca0d2302cc4fedb 100644 (file)
@@ -5,6 +5,9 @@
  *   Copyright (C) 2008 by Spencer Oliver                                  *
  *   spen@spen-soft.co.uk                                                  *
  *                                                                         *
+ *   Copyright (C) 2011 by Andreas Fritiofson                              *
+ *   andreas.fritiofson@gmail.com                                          *
+ *
  *   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     *
 
 /* stm32x register locations */
 
-#define STM32_FLASH_ACR                0x40022000
-#define STM32_FLASH_KEYR       0x40022004
-#define STM32_FLASH_OPTKEYR    0x40022008
-#define STM32_FLASH_SR         0x4002200C
-#define STM32_FLASH_CR         0x40022010
-#define STM32_FLASH_AR         0x40022014
-#define STM32_FLASH_OBR                0x4002201C
-#define STM32_FLASH_WRPR       0x40022020
+#define FLASH_REG_BASE_B0 0x40022000
+#define FLASH_REG_BASE_B1 0x40022040
+
+#define STM32_FLASH_ACR     0x00
+#define STM32_FLASH_KEYR    0x04
+#define STM32_FLASH_OPTKEYR 0x08
+#define STM32_FLASH_SR      0x0C
+#define STM32_FLASH_CR      0x10
+#define STM32_FLASH_AR      0x14
+#define STM32_FLASH_OBR     0x1C
+#define STM32_FLASH_WRPR    0x20
+
+/* TODO: Check if code using these really should be hard coded to bank 0.
+ * There are valid cases, on dual flash devices the protection of the
+ * second bank is done on the bank0 reg's. */
+#define STM32_FLASH_ACR_B0     0x40022000
+#define STM32_FLASH_KEYR_B0    0x40022004
+#define STM32_FLASH_OPTKEYR_B0 0x40022008
+#define STM32_FLASH_SR_B0      0x4002200C
+#define STM32_FLASH_CR_B0      0x40022010
+#define STM32_FLASH_AR_B0      0x40022014
+#define STM32_FLASH_OBR_B0     0x4002201C
+#define STM32_FLASH_WRPR_B0    0x40022020
 
 /* option byte location */
 
 #define KEY1                   0x45670123
 #define KEY2                   0xCDEF89AB
 
-/* we use an offset to access the second bank on dual flash devices
- * strangely the protection of the second bank is done on the bank0 reg's */
-
-#define FLASH_OFFSET_B0        0x00
-#define FLASH_OFFSET_B1 0x40
-
 struct stm32x_options
 {
        uint16_t RDP;
@@ -104,10 +116,8 @@ struct stm32x_flash_bank
        int probed;
 
        bool has_dual_banks;
-       /* used to access dual flash bank stm32xl
-        * 0x00 will address bank 0 flash
-        * 0x40 will address bank 1 flash */
-       int register_offset;
+       /* used to access dual flash bank stm32xl */
+       uint32_t register_base;
 };
 
 static int stm32x_mass_erase(struct flash_bank *bank);
@@ -130,7 +140,7 @@ FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command)
        stm32x_info->write_algorithm = NULL;
        stm32x_info->probed = 0;
        stm32x_info->has_dual_banks = false;
-       stm32x_info->register_offset = FLASH_OFFSET_B0;
+       stm32x_info->register_base = FLASH_REG_BASE_B0;
 
        return ERROR_OK;
 }
@@ -138,7 +148,7 @@ FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command)
 static inline int stm32x_get_flash_reg(struct flash_bank *bank, uint32_t reg)
 {
        struct stm32x_flash_bank *stm32x_info = bank->driver_priv;
-       return reg + stm32x_info->register_offset;
+       return reg + stm32x_info->register_base;
 }
 
 static inline int stm32x_get_flash_status(struct flash_bank *bank, uint32_t *status)
@@ -200,7 +210,7 @@ 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_offset != FLASH_OFFSET_B0)
+       if (stm32x_info->register_base != FLASH_REG_BASE_B0)
        {
                LOG_ERROR("Option Byte Operation's must use bank0");
                return ERROR_FLASH_OPERATION_FAILED;
@@ -218,7 +228,7 @@ static int stm32x_read_options(struct flash_bank *bank)
        stm32x_info = bank->driver_priv;
 
        /* read current option bytes */
-       int retval = target_read_u32(target, STM32_FLASH_OBR, &optiondata);
+       int retval = target_read_u32(target, STM32_FLASH_OBR_B0, &optiondata);
        if (retval != ERROR_OK)
                return retval;
 
@@ -229,7 +239,7 @@ static int stm32x_read_options(struct flash_bank *bank)
                LOG_INFO("Device Security Bit Set");
 
        /* each bit refers to a 4bank protection */
-       retval = target_read_u32(target, STM32_FLASH_WRPR, &optiondata);
+       retval = target_read_u32(target, STM32_FLASH_WRPR_B0, &optiondata);
        if (retval != ERROR_OK)
                return retval;
 
@@ -252,27 +262,27 @@ static int stm32x_erase_options(struct flash_bank *bank)
        stm32x_read_options(bank);
 
        /* unlock flash registers */
-       int retval = target_write_u32(target, STM32_FLASH_KEYR, KEY1);
+       int retval = target_write_u32(target, STM32_FLASH_KEYR_B0, KEY1);
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_write_u32(target, STM32_FLASH_KEYR, KEY2);
+       retval = target_write_u32(target, STM32_FLASH_KEYR_B0, KEY2);
        if (retval != ERROR_OK)
                return retval;
 
        /* unlock option flash registers */
-       retval = target_write_u32(target, STM32_FLASH_OPTKEYR, KEY1);
+       retval = target_write_u32(target, STM32_FLASH_OPTKEYR_B0, KEY1);
        if (retval != ERROR_OK)
                return retval;
-       retval = target_write_u32(target, STM32_FLASH_OPTKEYR, KEY2);
+       retval = target_write_u32(target, STM32_FLASH_OPTKEYR_B0, KEY2);
        if (retval != ERROR_OK)
                return retval;
 
        /* erase option bytes */
-       retval = target_write_u32(target, STM32_FLASH_CR, FLASH_OPTER | FLASH_OPTWRE);
+       retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_OPTER | FLASH_OPTWRE);
        if (retval != ERROR_OK)
                return retval;
-       retval = target_write_u32(target, STM32_FLASH_CR, FLASH_OPTER | FLASH_STRT | FLASH_OPTWRE);
+       retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_OPTER | FLASH_STRT | FLASH_OPTWRE);
        if (retval != ERROR_OK)
                return retval;
 
@@ -295,23 +305,23 @@ static int stm32x_write_options(struct flash_bank *bank)
        stm32x_info = bank->driver_priv;
 
        /* unlock flash registers */
-       int retval = target_write_u32(target, STM32_FLASH_KEYR, KEY1);
+       int retval = target_write_u32(target, STM32_FLASH_KEYR_B0, KEY1);
        if (retval != ERROR_OK)
                return retval;
-       retval = target_write_u32(target, STM32_FLASH_KEYR, KEY2);
+       retval = target_write_u32(target, STM32_FLASH_KEYR_B0, KEY2);
        if (retval != ERROR_OK)
                return retval;
 
        /* unlock option flash registers */
-       retval = target_write_u32(target, STM32_FLASH_OPTKEYR, KEY1);
+       retval = target_write_u32(target, STM32_FLASH_OPTKEYR_B0, KEY1);
        if (retval != ERROR_OK)
                return retval;
-       retval = target_write_u32(target, STM32_FLASH_OPTKEYR, KEY2);
+       retval = target_write_u32(target, STM32_FLASH_OPTKEYR_B0, KEY2);
        if (retval != ERROR_OK)
                return retval;
 
        /* program option bytes */
-       retval = target_write_u32(target, STM32_FLASH_CR, FLASH_OPTPG | FLASH_OPTWRE);
+       retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_OPTPG | FLASH_OPTWRE);
        if (retval != ERROR_OK)
                return retval;
 
@@ -369,7 +379,7 @@ static int stm32x_write_options(struct flash_bank *bank)
        if (retval != ERROR_OK)
                return retval;
 
-       retval = target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK);
+       retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_LOCK);
        if (retval != ERROR_OK)
                return retval;
 
@@ -398,7 +408,7 @@ static int stm32x_protect_check(struct flash_bank *bank)
 
        /* medium density - each bit refers to a 4bank protection
         * high density - each bit refers to a 2bank protection */
-       retval = target_read_u32(target, STM32_FLASH_WRPR, &protection);
+       retval = target_read_u32(target, STM32_FLASH_WRPR_B0, &protection);
        if (retval != ERROR_OK)
                return retval;
 
@@ -544,7 +554,7 @@ static int stm32x_protect(struct flash_bank *bank, int set, int first, int last)
 
        /* medium density - each bit refers to a 4bank protection
         * high density - each bit refers to a 2bank protection */
-       retval = target_read_u32(target, STM32_FLASH_WRPR, &protection);
+       retval = target_read_u32(target, STM32_FLASH_WRPR_B0, &protection);
        if (retval != ERROR_OK)
                return retval;
 
@@ -616,34 +626,45 @@ static int stm32x_write_block(struct flash_bank *bank, uint8_t *buffer,
        uint32_t buffer_size = 16384;
        struct working_area *source;
        uint32_t address = bank->base + offset;
-       struct reg_param reg_params[4];
+       struct reg_param reg_params[5];
        struct armv7m_algorithm armv7m_info;
        int retval = ERROR_OK;
 
-       /* see contib/loaders/flash/stm32x.s for src */
+       /* see contrib/loaders/flash/stm32f1x.S for src */
 
        static const uint8_t stm32x_flash_write_code[] = {
-                                                                       /* #define STM32_FLASH_CR_OFFSET        0x10 */
-                                                                       /* #define STM32_FLASH_SR_OFFSET        0x0C */
-                                                                       /* write: */
-               0x08, 0x4c,                                     /* ldr  r4, STM32_FLASH_BASE */
-               0x1c, 0x44,                                     /* add  r4, r3 */
-                                                                       /* write_half_word: */
-               0x01, 0x23,                                     /* movs r3, #0x01 */
-               0x23, 0x61,                                     /* str  r3, [r4, #STM32_FLASH_CR_OFFSET] */
-               0x30, 0xf8, 0x02, 0x3b,         /* ldrh r3, [r0], #0x02 */
-               0x21, 0xf8, 0x02, 0x3b,         /* strh r3, [r1], #0x02 */
-                                                                       /* busy: */
-               0xe3, 0x68,                                     /* ldr  r3, [r4, #STM32_FLASH_SR_OFFSET] */
-               0x13, 0xf0, 0x01, 0x0f,         /* tst  r3, #0x01 */
-               0xfb, 0xd0,                                     /* beq  busy */
-               0x13, 0xf0, 0x14, 0x0f,         /* tst  r3, #0x14 */
-               0x01, 0xd1,                                     /* bne  exit */
-               0x01, 0x3a,                                     /* subs r2, r2, #0x01 */
-               0xf0, 0xd1,                                     /* bne  write_half_word */
-                                                                       /* exit: */
-               0x00, 0xbe,                                     /* bkpt #0x00 */
-               0x00, 0x20, 0x02, 0x40,         /* STM32_FLASH_BASE: .word 0x40022000 */
+               /* #define STM32_FLASH_CR_OFFSET 0x10 */
+               /* #define STM32_FLASH_SR_OFFSET 0x0C */
+               /* wait_fifo: */
+                       0x16, 0x68,             /* ldr          r6, [r2, #0] */
+                       0x00, 0x2e,             /* cmp          r6, #0 */
+                       0x1a, 0xd0,             /* beq          exit */
+                       0x55, 0x68,             /* ldr          r5, [r2, #4] */
+                       0xb5, 0x42,             /* cmp          r5, r6 */
+                       0xf9, 0xd0,             /* beq          wait_fifo */
+                       0x01, 0x26,             /* movs         r6, #1 */
+                       0x06, 0x61,             /* str          r6, [r0, #STM32_FLASH_CR_OFFSET] */
+                       0x35, 0xf8, 0x02, 0x6b, /* ldrh         r6, [r5], #2 */
+                       0x24, 0xf8, 0x02, 0x6b, /* strh         r6, [r4], #2 */
+               /* busy: */
+                       0xc6, 0x68,             /* ldr          r6, [r0, #STM32_FLASH_SR_OFFSET] */
+                       0x16, 0xf0, 0x01, 0x0f, /* tst          r6, #1 */
+                       0xfb, 0xd1,             /* bne          busy */
+                       0x16, 0xf0, 0x14, 0x0f, /* tst          r6, #0x14 */
+                       0x07, 0xd1,             /* bne          error */
+                       0x9d, 0x42,             /* cmp          r5, r3 */
+                       0x28, 0xbf,             /* it           cs */
+                       0x02, 0xf1, 0x08, 0x05, /* addcs        r5, r2, #8 */
+                       0x55, 0x60,             /* str          r5, [r2, #4] */
+                       0x01, 0x39,             /* subs         r1, r1, #1 */
+                       0x19, 0xb1,             /* cbz          r1, exit */
+                       0xe4, 0xe7,             /* b            wait_fifo */
+               /* error: */
+                       0x00, 0x20,             /* movs         r0, #0 */
+                       0xc2, 0xf8,     0x02, 0x00, /* str      r0, [r2, #2] */
+               /* exit: */
+                       0x30, 0x46,             /* mov          r0, r6 */
+                       0x00, 0xbe,             /* bkpt         #0 */
        };
 
        /* flash write code */
@@ -663,6 +684,7 @@ static int stm32x_write_block(struct flash_bank *bank, uint8_t *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)
                {
                        /* if we already allocated the writing code, but failed to get a
@@ -675,60 +697,152 @@ static int stm32x_write_block(struct flash_bank *bank, uint8_t *buffer,
                }
        };
 
+       /* Set up working area. First word is write pointer, second word is read pointer,
+        * rest is fifo data area. */
+       uint32_t wp_addr = source->address;
+       uint32_t rp_addr = source->address + 4;
+       uint32_t fifo_start_addr = source->address + 8;
+       uint32_t fifo_end_addr = source->address + source->size;
+
+       uint32_t wp = fifo_start_addr;
+       uint32_t rp = fifo_start_addr;
+
+       retval = target_write_u32(target, wp_addr, wp);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u32(target, rp_addr, rp);
+       if (retval != ERROR_OK)
+               return retval;
+
+       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 */
+       init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);    /* buffer end */
+       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[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);
+
        armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
        armv7m_info.core_mode = ARMV7M_MODE_ANY;
 
-       init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
-       init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
-       init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
-       init_reg_param(&reg_params[3], "r3", 32, PARAM_IN_OUT);
+       /* Start up algorithm on target and let it idle while writing the first chunk */
+       if ((retval = target_start_algorithm(target, 0, NULL, 5, reg_params,
+                       stm32x_info->write_algorithm->address,
+                       0,
+                       &armv7m_info)) != ERROR_OK)
+       {
+               LOG_ERROR("error starting stm32x flash write algorithm");
+               goto cleanup;
+       }
 
        while (count > 0)
        {
-               uint32_t thisrun_count = (count > (buffer_size / 2)) ?
-                               (buffer_size / 2) : count;
-
-               if ((retval = target_write_buffer(target, source->address,
-                               thisrun_count * 2, buffer)) != ERROR_OK)
+               retval = target_read_u32(target, rp_addr, &rp);
+               if (retval != ERROR_OK)
+               {
+                       LOG_ERROR("failed to get read pointer");
                        break;
+               }
 
-               buf_set_u32(reg_params[0].value, 0, 32, source->address);
-               buf_set_u32(reg_params[1].value, 0, 32, address);
-               buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
-               buf_set_u32(reg_params[3].value, 0, 32, stm32x_info->register_offset);
+               LOG_DEBUG("count 0x%"PRIx32" wp 0x%"PRIx32" rp 0x%"PRIx32, count, wp, rp);
 
-               if ((retval = target_run_algorithm(target, 0, NULL, 4, reg_params,
-                               stm32x_info->write_algorithm->address,
-                               0,
-                               10000, &armv7m_info)) != ERROR_OK)
+               if (rp == 0)
                {
-                       LOG_ERROR("error executing stm32x flash write algorithm");
+                       LOG_ERROR("flash write algorithm aborted by target");
+                       retval = ERROR_FLASH_OPERATION_FAILED;
                        break;
                }
 
-               if (buf_get_u32(reg_params[3].value, 0, 32) & FLASH_PGERR)
+               if ((rp & 1) || rp < fifo_start_addr || rp >= fifo_end_addr)
+               {
+                       LOG_ERROR("corrupted fifo read pointer 0x%"PRIx32, rp);
+                       break;
+               }
+
+               /* Count the number of bytes available in the fifo without
+                * crossing the wrap around. Make sure to not fill it completely,
+                * because that would make wp == rp and that's the empty condition. */
+               uint32_t thisrun_bytes;
+               if (rp > wp)
+                       thisrun_bytes = rp - wp - 2;
+               else if (rp > fifo_start_addr)
+                       thisrun_bytes = fifo_end_addr - wp;
+               else
+                       thisrun_bytes = fifo_end_addr - wp - 2;
+
+               if (thisrun_bytes == 0)
+               {
+                       /* Throttle polling a bit if transfer is (much) faster than flash
+                        * programming. The exact delay shouldn't matter as long as it's
+                        * less than buffer size / flash speed. This is very unlikely to
+                        * run when using high latency connections such as USB. */
+                       alive_sleep(10);
+                       continue;
+               }
+
+               /* Limit to the amount of data we actually want to write */
+               if (thisrun_bytes > count * 2)
+                       thisrun_bytes = count * 2;
+
+               /* Write data to fifo */
+               retval = target_write_buffer(target, wp, thisrun_bytes, buffer);
+               if (retval != ERROR_OK)
+                       break;
+
+               /* Update counters and wrap write pointer */
+               buffer += thisrun_bytes;
+               count -= thisrun_bytes / 2;
+               wp += thisrun_bytes;
+               if (wp >= fifo_end_addr)
+                       wp = fifo_start_addr;
+
+               /* Store updated write pointer to target */
+               retval = target_write_u32(target, wp_addr, wp);
+               if (retval != ERROR_OK)
+                       break;
+       }
+
+       if (retval != ERROR_OK)
+       {
+               /* abort flash write algorithm on target */
+               target_write_u32(target, wp_addr, 0);
+       }
+
+       int retval2;
+       if ((retval2 = target_wait_algorithm(target, 0, NULL, 5, reg_params,
+                       0,
+                       10000,
+                       &armv7m_info)) != ERROR_OK)
+       {
+               LOG_ERROR("error waiting for stm32x flash write algorithm");
+               retval = retval2;
+       }
+
+       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, STM32_FLASH_SR, FLASH_PGERR);
-                       retval = ERROR_FAIL;
-                       break;
+                       target_write_u32(target, STM32_FLASH_SR_B0, FLASH_PGERR);
                }
 
-               if (buf_get_u32(reg_params[3].value, 0, 32) & FLASH_WRPRTERR)
+               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, STM32_FLASH_SR, FLASH_WRPRTERR);
-                       retval = ERROR_FAIL;
-                       break;
+                       target_write_u32(target, STM32_FLASH_SR_B0, FLASH_WRPRTERR);
                }
-
-               buffer += thisrun_count * 2;
-               address += thisrun_count * 2;
-               count -= thisrun_count;
        }
 
+cleanup:
        target_free_working_area(target, source);
        target_free_working_area(target, stm32x_info->write_algorithm);
 
@@ -736,6 +850,7 @@ static int stm32x_write_block(struct flash_bank *bank, uint8_t *buffer,
        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;
 }
@@ -832,7 +947,7 @@ static int stm32x_write(struct flash_bank *bank, uint8_t *buffer,
                        return retval;
        }
 
-       return target_write_u32(target, STM32_FLASH_CR, FLASH_LOCK);
+       return target_write_u32(target, STM32_FLASH_CR_B0, FLASH_LOCK);
 }
 
 static int stm32x_probe(struct flash_bank *bank)
@@ -846,7 +961,7 @@ static int stm32x_probe(struct flash_bank *bank)
        uint32_t base_address = 0x08000000;
 
        stm32x_info->probed = 0;
-       stm32x_info->register_offset = FLASH_OFFSET_B0;
+       stm32x_info->register_base = FLASH_REG_BASE_B0;
 
        /* read stm32 device id register */
        int retval = target_read_u32(target, 0xE0042000, &device_id);
@@ -938,6 +1053,22 @@ static int stm32x_probe(struct flash_bank *bank)
                        num_pages = 128;
                }
        }
+       else if ((device_id & 0x7ff) == 0x428)
+       {
+               /* value line density - we have 1k pages
+                * 4 pages for a protection area */
+               page_size = 2048;
+               stm32x_info->ppage_size = 4;
+
+               /* check for early silicon */
+               if (num_pages == 0xffff)
+               {
+                       /* number of sectors may be incorrrect on early silicon */
+                       LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming 128k flash");
+                       num_pages = 128;
+               }
+       }
+
        else if ((device_id & 0x7ff) == 0x430)
        {
                /* xl line density - we have 2k pages
@@ -964,7 +1095,7 @@ static int stm32x_probe(struct flash_bank *bank)
                {
                        num_pages -= 512;
                        /* bank1 also uses a register offset */
-                       stm32x_info->register_offset = FLASH_OFFSET_B1;
+                       stm32x_info->register_base = FLASH_REG_BASE_B1;
                        base_address = 0x08080000;
                }
        }
@@ -1138,6 +1269,27 @@ static int get_stm32x_info(struct flash_bank *bank, char *buf, int buf_size)
                                break;
                }
        }
+       else if ((device_id & 0x7ff) == 0x428)
+       {
+               printed = snprintf(buf, buf_size, "stm32x (Value HD) - Rev: ");
+               buf += printed;
+               buf_size -= printed;
+
+               switch (device_id >> 16)
+               {
+                       case 0x1000:
+                               snprintf(buf, buf_size, "A");
+                               break;
+
+                       case 0x1001:
+                               snprintf(buf, buf_size, "Z");
+                               break;
+
+                       default:
+                               snprintf(buf, buf_size, "unknown");
+                               break;
+               }
+       }
        else if ((device_id & 0x7ff) == 0x430)
        {
                printed = snprintf(buf, buf_size, "stm32x (XL) - Rev: ");
@@ -1291,7 +1443,7 @@ COMMAND_HANDLER(stm32x_handle_options_read_command)
        if (ERROR_OK != retval)
                return retval;
 
-       retval = target_read_u32(target, STM32_FLASH_OBR, &optionbyte);
+       retval = target_read_u32(target, STM32_FLASH_OBR_B0, &optionbyte);
        if (retval != ERROR_OK)
                return retval;
        command_print(CMD_CTX, "Option Byte: 0x%" PRIx32 "", optionbyte);

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)