#include "general.h"
#include "target.h"
#include "target_internal.h"
#include "cortexm.h"
static bool stm32f1_cmd_option(target_s *target, int argc, const char **argv);
const command_s stm32f1_cmd_list[] = {
{"option", stm32f1_cmd_option, "Manipulate option bytes"},
{NULL, NULL, NULL},
};
static bool stm32f1_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len);
static bool stm32f1_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len);
static bool stm32f1_mass_erase(target_s *target);
#define FPEC_BASE 0x40022000U
#define FLASH_ACR (FPEC_BASE + 0x00U)
#define FLASH_KEYR (FPEC_BASE + 0x04U)
#define FLASH_OPTKEYR (FPEC_BASE + 0x08U)
#define FLASH_SR (FPEC_BASE + 0x0cU)
#define FLASH_CR (FPEC_BASE + 0x10U)
#define FLASH_AR (FPEC_BASE + 0x14U)
#define FLASH_OBR (FPEC_BASE + 0x1cU)
#define FLASH_WRPR (FPEC_BASE + 0x20U)
#define FLASH_BANK1_OFFSET 0x00U
#define FLASH_BANK2_OFFSET 0x40U
#define FLASH_BANK_SPLIT 0x08080000U
#define FLASH_CR_OBL_LAUNCH (1U << 13U)
#define FLASH_CR_OPTWRE (1U << 9U)
#define FLASH_CR_LOCK (1U << 7U)
#define FLASH_CR_STRT (1U << 6U)
#define FLASH_CR_OPTER (1U << 5U)
#define FLASH_CR_OPTPG (1U << 4U)
#define FLASH_CR_MER (1U << 2U)
#define FLASH_CR_PER (1U << 1U)
#define FLASH_CR_PG (1U << 0U)
#define FLASH_OBR_RDPRT (1U << 1U)
#define FLASH_SR_BSY (1U << 0U)
#define FLASH_OBP_RDP 0x1ffff800U
#define FLASH_OBP_RDP_KEY 0x5aa5U
#define FLASH_OBP_RDP_KEY_F3 0x55aaU
#define KEY1 0x45670123U
#define KEY2 0xcdef89abU
#define SR_ERROR_MASK 0x14U
#define SR_PROG_ERROR 0x04U
#define SR_EOP 0x20U
#define DBGMCU_IDCODE 0xe0042000U
#define DBGMCU_IDCODE_F0 0x40015800U
#define DBGMCU_IDCODE_GD32E5 0xe0044000U
#define GD32Fx_FLASHSIZE 0x1ffff7e0U
#define GD32F0_FLASHSIZE 0x1ffff7ccU
#define AT32F4x_IDCODE_SERIES_MASK 0xfffff000U
#define AT32F4x_IDCODE_PART_MASK 0x00000fffU
#define AT32F41_SERIES 0x70030000U
#define AT32F40_SERIES 0x70050000U
#define AT32F43_SERIES_4K 0x70084000U
#define AT32F43_SERIES_2K 0x70083000U
#define DBGMCU_IDCODE_MM32L0 0x40013400U
#define DBGMCU_IDCODE_MM32F3 0x40007080U
static void stm32f1_add_flash(target_s *target, uint32_t addr, size_t length, size_t erasesize)
{
target_flash_s *flash = calloc(1, sizeof(*flash));
if (!flash) {
DEBUG_ERROR("calloc: failed in %s\n", __func__);
return;
}
flash->start = addr;
flash->length = length;
flash->blocksize = erasesize;
flash->writesize = 1024U;
flash->erase = stm32f1_flash_erase;
flash->write = stm32f1_flash_write;
flash->erased = 0xff;
target_add_flash(target, flash);
}
static uint16_t stm32f1_read_idcode(target_s *const target)
{
if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M0 ||
(target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M23)
return target_mem_read32(target, DBGMCU_IDCODE_F0) & 0xfffU;
if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M33)
return target_mem_read32(target, DBGMCU_IDCODE_GD32E5) & 0xfffU;
return target_mem_read32(target, DBGMCU_IDCODE) & 0xfffU;
}
bool gd32f1_probe(target_s *target)
{
const uint16_t device_id = stm32f1_read_idcode(target);
size_t block_size = 0x400;
switch (device_id) {
case 0x414U:
case 0x430U:
target->driver = "GD32F3";
break;
case 0x418U:
target->driver = "GD32F2";
break;
case 0x410U:
if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M23)
target->driver = "GD32E230";
else if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M4)
target->driver = "GD32F3";
else
target->driver = "GD32F1";
break;
case 0x444U:
target->driver = "GD32E5";
block_size = 0x2000;
break;
default:
return false;
}
const uint32_t signature = target_mem_read32(target, GD32Fx_FLASHSIZE);
const uint16_t flash_size = signature & 0xffffU;
const uint16_t ram_size = signature >> 16U;
target->part_id = device_id;
target->mass_erase = stm32f1_mass_erase;
target_add_ram(target, 0x20000000, ram_size * 1024U);
stm32f1_add_flash(target, 0x8000000, (size_t)flash_size * 1024U, block_size);
target_add_commands(target, stm32f1_cmd_list, target->driver);
return true;
}
static bool at32f40_detect(target_s *target, const uint16_t part_id)
{
switch (part_id) {
case 0x0240U: case 0x0241U: case 0x0242U: case 0x0243U: case 0x0249U: case 0x024aU: case 0x0254U: case 0x02cdU: case 0x02ceU: case 0x02cfU: case 0x02d0U: case 0x02d1U: case 0x02d2U: case 0x0344U: case 0x0345U: case 0x0346U: case 0x0347U: case 0x034bU: case 0x034cU: case 0x0353U: stm32f1_add_flash(target, 0x08000000, 256U * 1024U, 2U * 1024U);
break;
default:
return false;
}
target_add_ram(target, 0x20000000, 96U * 1024U);
target->driver = "AT32F403A/407";
target->mass_erase = stm32f1_mass_erase;
return true;
}
static bool at32f41_detect(target_s *target, const uint16_t part_id)
{
switch (part_id) {
case 0x0240U: case 0x0241U: case 0x0242U: case 0x0243U: case 0x024cU: stm32f1_add_flash(target, 0x08000000, 256U * 1024U, 2U * 1024U);
break;
case 0x01c4U: case 0x01c5U: case 0x01c6U: case 0x01c7U: case 0x01cdU: stm32f1_add_flash(target, 0x08000000, 128U * 1024U, 2U * 1024U);
break;
case 0x0108U: case 0x0109U: case 0x010aU: stm32f1_add_flash(target, 0x08000000, 64U * 1024U, 2U * 1024U);
break;
default:
return false;
}
target_add_ram(target, 0x20000000, 32U * 1024U);
target->driver = "AT32F415";
target->mass_erase = stm32f1_mass_erase;
return true;
}
static bool at32f43_detect(target_s *target, const uint16_t part_id)
{
uint32_t flash_size_kb = 0;
uint32_t sector_size = 0;
switch (part_id) {
case 0x0540U: case 0x0543U: case 0x0546U: case 0x0549U: case 0x054cU: case 0x054fU: case 0x0552U: case 0x0555U: flash_size_kb = 4032;
sector_size = 4096;
break;
case 0x0598U: case 0x0599U: case 0x059aU: case 0x059bU: case 0x059cU: case 0x059dU: case 0x059eU: case 0x059fU: flash_size_kb = 448;
sector_size = 4096;
break;
case 0x0341U: case 0x0344U: case 0x0347U: case 0x034aU: case 0x034dU: case 0x0350U: case 0x0353U: case 0x0356U: flash_size_kb = 1024;
sector_size = 2048;
break;
case 0x0242U: case 0x0245U: case 0x0248U: case 0x024bU: case 0x024eU: case 0x0251U: case 0x0254U: case 0x0257U: flash_size_kb = 256;
sector_size = 2048;
break;
default:
return false;
}
stm32f1_add_flash(target, 0x08000000, flash_size_kb * 1024U, sector_size);
target_add_ram(target, 0x20000000, 64U * 1024U);
target_add_ram(target, 0x20010000, 320U * 1024U);
target->driver = "AT32F435";
target->mass_erase = stm32f1_mass_erase;
return true;
}
bool at32fxx_probe(target_s *target)
{
if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) != CORTEX_M4)
return false;
const uint32_t idcode = target_mem_read32(target, DBGMCU_IDCODE);
const uint32_t series = idcode & AT32F4x_IDCODE_SERIES_MASK;
const uint16_t part_id = idcode & AT32F4x_IDCODE_PART_MASK;
if (series == AT32F40_SERIES)
return at32f40_detect(target, part_id);
if (series == AT32F41_SERIES)
return at32f41_detect(target, part_id);
if (series == AT32F43_SERIES_4K || series == AT32F43_SERIES_2K)
return at32f43_detect(target, part_id);
return false;
}
void mm32l0_mem_write_sized(adiv5_access_port_s *ap, uint32_t dest, const void *src, size_t len, align_e align)
{
uint32_t odest = dest;
len >>= align;
ap_mem_access_setup(ap, dest, align);
while (len--) {
uint32_t tmp = 0;
switch (align) {
case ALIGN_8BIT: {
uint8_t value;
memcpy(&value, src, sizeof(value));
tmp = (uint32_t)value;
tmp = tmp | tmp << 8U;
tmp = tmp | tmp << 16U;
break;
}
case ALIGN_16BIT: {
uint16_t value;
memcpy(&value, src, sizeof(value));
tmp = (uint32_t)value;
tmp = tmp | tmp << 16U;
break;
}
case ALIGN_64BIT:
case ALIGN_32BIT:
memcpy(&tmp, src, sizeof(tmp));
break;
}
src = (uint8_t *)src + (1 << align);
dest += (1 << align);
adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_AP_DRW, tmp);
if ((dest ^ odest) & 0xfffffc00U) {
odest = dest;
adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_AP_TAR, dest);
}
}
adiv5_dp_read(ap->dp, ADIV5_DP_RDBUFF);
}
bool mm32l0xx_probe(target_s *target)
{
const char *name;
size_t flash_kbyte = 0;
size_t ram_kbyte = 0;
size_t block_size = 0x400U;
const uint32_t mm32_id = target_mem_read32(target, DBGMCU_IDCODE_MM32L0);
if (target_check_error(target)) {
DEBUG_ERROR("%s: read error at 0x%" PRIx32 "\n", __func__, (uint32_t)DBGMCU_IDCODE_MM32L0);
return false;
}
switch (mm32_id) {
case 0xcc568091U:
name = "MM32L07x";
flash_kbyte = 128;
ram_kbyte = 8;
break;
case 0xcc4460b1:
name = "MM32SPIN05";
flash_kbyte = 32;
ram_kbyte = 4;
break;
case 0xcc56a097U:
name = "MM32SPIN27";
flash_kbyte = 128;
ram_kbyte = 12;
break;
case 0x00000000U:
case 0xffffffffU:
return false;
default:
DEBUG_WARN("%s: unknown mm32 dev_id 0x%" PRIx32 "\n", __func__, mm32_id);
return false;
}
target->part_id = mm32_id & 0xfffU;
target->driver = name;
target->mass_erase = stm32f1_mass_erase;
target_add_ram(target, 0x20000000U, ram_kbyte * 1024U);
stm32f1_add_flash(target, 0x08000000U, flash_kbyte * 1024U, block_size);
target_add_commands(target, stm32f1_cmd_list, name);
cortex_ap(target)->dp->mem_write = mm32l0_mem_write_sized;
return true;
}
bool mm32f3xx_probe(target_s *target)
{
const char *name;
size_t flash_kbyte = 0;
size_t ram1_kbyte = 0;
size_t ram2_kbyte = 0;
size_t block_size = 0x400U;
const uint32_t mm32_id = target_mem_read32(target, DBGMCU_IDCODE_MM32F3);
if (target_check_error(target)) {
DEBUG_ERROR("%s: read error at 0x%" PRIx32 "\n", __func__, (uint32_t)DBGMCU_IDCODE_MM32F3);
return false;
}
switch (mm32_id) {
case 0xcc9aa0e7U:
name = "MM32F3273";
flash_kbyte = 512;
ram1_kbyte = 128;
break;
case 0x4d4d0800U:
name = "MM32F5277";
flash_kbyte = 256;
ram1_kbyte = 32;
ram2_kbyte = 128;
break;
case 0x00000000U:
case 0xffffffffU:
return false;
default:
DEBUG_WARN("%s: unknown mm32 ID code 0x%" PRIx32 "\n", __func__, mm32_id);
return false;
}
target->part_id = mm32_id & 0xfffU;
target->driver = name;
target->mass_erase = stm32f1_mass_erase;
if (ram1_kbyte != 0)
target_add_ram(target, 0x20000000U, ram1_kbyte * 1024U);
if (ram2_kbyte != 0)
target_add_ram(target, 0x30000000U, ram2_kbyte * 1024U);
stm32f1_add_flash(target, 0x08000000U, flash_kbyte * 1024U, block_size);
target_add_commands(target, stm32f1_cmd_list, name);
return true;
}
bool stm32f1_probe(target_s *target)
{
const uint16_t device_id = stm32f1_read_idcode(target);
target->mass_erase = stm32f1_mass_erase;
size_t flash_size = 0;
size_t block_size = 0x400;
switch (device_id) {
case 0x29bU:
case 0x410U:
case 0x412U:
case 0x420U:
target_add_ram(target, 0x20000000, 0x5000);
stm32f1_add_flash(target, 0x8000000, 0x20000, 0x400);
target_add_commands(target, stm32f1_cmd_list, "STM32 LD/MD/VL-LD/VL-MD");
adiv5_access_port_s *ap = cortex_ap(target);
if ((ap->idr >> 28U) > 1U) {
target->driver = "STM32F1 (clone) medium density";
DEBUG_WARN("Detected clone STM32F1\n");
} else
target->driver = "STM32F1 medium density";
target->part_id = device_id;
return true;
case 0x414U:
case 0x418U:
case 0x428U:
target->driver = "STM32F1 VL density";
target->part_id = device_id;
target_add_ram(target, 0x20000000, 0x10000);
stm32f1_add_flash(target, 0x8000000, 0x80000, 0x800);
target_add_commands(target, stm32f1_cmd_list, "STM32 HF/CL/VL-HD");
return true;
case 0x430U:
target->driver = "STM32F1 XL density";
target->part_id = device_id;
target_add_ram(target, 0x20000000, 0x18000);
stm32f1_add_flash(target, 0x8000000, 0x80000, 0x800);
stm32f1_add_flash(target, 0x8080000, 0x80000, 0x800);
target_add_commands(target, stm32f1_cmd_list, "STM32 XL/VL-XL");
return true;
case 0x438U:
case 0x422U:
case 0x446U:
target_add_ram(target, 0x10000000, 0x4000);
case 0x432U:
case 0x439U:
target->driver = "STM32F3";
target->part_id = device_id;
target_add_ram(target, 0x20000000, 0x10000);
stm32f1_add_flash(target, 0x8000000, 0x80000, 0x800);
target_add_commands(target, stm32f1_cmd_list, "STM32F3");
return true;
case 0x444U:
target->driver = "STM32F03";
flash_size = 0x8000;
break;
case 0x445U:
target->driver = "STM32F04/F070x6";
flash_size = 0x8000;
break;
case 0x440U:
target->driver = "STM32F05/F030x8";
flash_size = 0x10000;
break;
case 0x448U:
target->driver = "STM32F07";
flash_size = 0x20000;
block_size = 0x800;
break;
case 0x442U:
target->driver = "STM32F09/F030xc";
flash_size = 0x40000;
block_size = 0x800;
break;
default:
return false;
}
target->part_id = device_id;
target_add_ram(target, 0x20000000, 0x5000);
stm32f1_add_flash(target, 0x8000000, flash_size, block_size);
target_add_commands(target, stm32f1_cmd_list, "STM32F0");
return true;
}
static bool stm32f1_flash_unlock(target_s *target, uint32_t bank_offset)
{
target_mem_write32(target, FLASH_KEYR + bank_offset, KEY1);
target_mem_write32(target, FLASH_KEYR + bank_offset, KEY2);
uint32_t ctrl = target_mem_read32(target, FLASH_CR);
if (ctrl & FLASH_CR_LOCK)
DEBUG_ERROR("unlock failed, cr: 0x%08" PRIx32 "\n", ctrl);
return !(ctrl & FLASH_CR_LOCK);
}
static inline void stm32f1_flash_clear_eop(target_s *const target, const uint32_t bank_offset)
{
const uint32_t status = target_mem_read32(target, FLASH_SR + bank_offset);
target_mem_write32(target, FLASH_SR + bank_offset, status | SR_EOP);
}
static bool stm32f1_flash_busy_wait(
target_s *const target, const uint32_t bank_offset, platform_timeout_s *const timeout)
{
uint32_t status = FLASH_SR_BSY;
while (!(status & SR_EOP) && (status & FLASH_SR_BSY)) {
status = target_mem_read32(target, FLASH_SR + bank_offset);
if (target_check_error(target)) {
DEBUG_ERROR("Lost communications with target");
return false;
}
if (timeout)
target_print_progress(timeout);
};
if (status & SR_ERROR_MASK)
DEBUG_ERROR("stm32f1 flash error 0x%" PRIx32 "\n", status);
return !(status & SR_ERROR_MASK);
}
static uint32_t stm32f1_bank_offset_for(target_addr_t addr)
{
if (addr >= FLASH_BANK_SPLIT)
return FLASH_BANK2_OFFSET;
return FLASH_BANK1_OFFSET;
}
static bool stm32f1_flash_erase(target_flash_s *flash, target_addr_t addr, size_t length)
{
target_s *target = flash->t;
target_addr_t end = addr + length - 1U;
DEBUG_TARGET("%s: at %08" PRIx32 "\n", __func__, addr);
if ((target->part_id == 0x430U && end >= FLASH_BANK_SPLIT && !stm32f1_flash_unlock(target, FLASH_BANK2_OFFSET)) ||
(addr < FLASH_BANK_SPLIT && !stm32f1_flash_unlock(target, 0)))
return false;
const uint32_t bank_offset = stm32f1_bank_offset_for(addr);
stm32f1_flash_clear_eop(target, bank_offset);
target_mem_write32(target, FLASH_CR + bank_offset, FLASH_CR_PER);
target_mem_write32(target, FLASH_AR + bank_offset, addr);
target_mem_write32(target, FLASH_CR + bank_offset, FLASH_CR_STRT | FLASH_CR_PER);
return stm32f1_flash_busy_wait(target, bank_offset, NULL);
}
static size_t stm32f1_bank1_length(target_addr_t addr, size_t len)
{
if (addr >= FLASH_BANK_SPLIT)
return 0;
if (addr + len > FLASH_BANK_SPLIT)
return FLASH_BANK_SPLIT - addr;
return len;
}
static bool stm32f1_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len)
{
target_s *target = flash->t;
const size_t offset = stm32f1_bank1_length(dest, len);
DEBUG_TARGET("%s: at %08" PRIx32 " for %zu bytes\n", __func__, dest, len);
if (offset) {
stm32f1_flash_clear_eop(target, FLASH_BANK1_OFFSET);
target_mem_write32(target, FLASH_CR, FLASH_CR_PG);
cortexm_mem_write_sized(target, dest, src, offset, ALIGN_16BIT);
if (!stm32f1_flash_busy_wait(target, FLASH_BANK1_OFFSET, NULL))
return false;
}
const size_t remainder = len - offset;
if (target->part_id == 0x430U && remainder) {
const uint8_t *data = src;
stm32f1_flash_clear_eop(target, FLASH_BANK2_OFFSET);
target_mem_write32(target, FLASH_CR + FLASH_BANK2_OFFSET, FLASH_CR_PG);
cortexm_mem_write_sized(target, dest + offset, data + offset, remainder, ALIGN_16BIT);
if (!stm32f1_flash_busy_wait(target, FLASH_BANK2_OFFSET, NULL))
return false;
}
return true;
}
static bool stm32f1_mass_erase_bank(
target_s *const target, const uint32_t bank_offset, platform_timeout_s *const timeout)
{
if (!stm32f1_flash_unlock(target, bank_offset))
return false;
stm32f1_flash_clear_eop(target, bank_offset);
target_mem_write32(target, FLASH_CR + bank_offset, FLASH_CR_MER);
target_mem_write32(target, FLASH_CR + bank_offset, FLASH_CR_STRT | FLASH_CR_MER);
return stm32f1_flash_busy_wait(target, bank_offset, timeout);
}
static bool stm32f1_mass_erase(target_s *target)
{
if (!stm32f1_flash_unlock(target, 0))
return false;
platform_timeout_s timeout;
platform_timeout_set(&timeout, 500);
if (!stm32f1_mass_erase_bank(target, FLASH_BANK1_OFFSET, &timeout))
return false;
if (target->part_id == 0x430U)
return stm32f1_mass_erase_bank(target, FLASH_BANK2_OFFSET, &timeout);
return true;
}
static uint16_t stm32f1_flash_readable_key(const target_s *const target)
{
switch (target->part_id) {
case 0x422U:
case 0x432U:
case 0x438U:
case 0x440U:
case 0x446U:
case 0x445U:
case 0x448U:
case 0x442U:
return FLASH_OBP_RDP_KEY_F3;
}
return FLASH_OBP_RDP_KEY;
}
static bool stm32f1_option_erase(target_s *target)
{
stm32f1_flash_clear_eop(target, FLASH_BANK1_OFFSET);
target_mem_write32(target, FLASH_CR, FLASH_CR_OPTER | FLASH_CR_OPTWRE);
target_mem_write32(target, FLASH_CR, FLASH_CR_STRT | FLASH_CR_OPTER | FLASH_CR_OPTWRE);
return stm32f1_flash_busy_wait(target, FLASH_BANK1_OFFSET, NULL);
}
static bool stm32f1_option_write_erased(
target_s *const target, const size_t offset, const uint16_t value, const bool write16_broken)
{
if (value == 0xffffU)
return true;
stm32f1_flash_clear_eop(target, FLASH_BANK1_OFFSET);
target_mem_write32(target, FLASH_CR, FLASH_CR_OPTPG | FLASH_CR_OPTWRE);
const uint32_t addr = FLASH_OBP_RDP + (offset * 2U);
if (write16_broken)
target_mem_write32(target, addr, 0xffff0000U | value);
else
target_mem_write16(target, addr, value);
const bool result = stm32f1_flash_busy_wait(target, FLASH_BANK1_OFFSET, NULL);
if (result || offset != 0U)
return result;
const uint8_t status = target_mem_read32(target, FLASH_SR) & SR_ERROR_MASK;
return status == SR_PROG_ERROR;
}
static bool stm32f1_option_write(target_s *const target, const uint32_t addr, const uint16_t value)
{
const uint32_t index = (addr - FLASH_OBP_RDP) >> 1U;
if (index > 7U)
return false;
uint16_t opt_val[8];
for (size_t i = 0U; i < 16U; i += 4U) {
const size_t offset = i >> 1U;
uint32_t val = target_mem_read32(target, FLASH_OBP_RDP + i);
opt_val[offset] = val & 0xffffU;
opt_val[offset + 1U] = val >> 16U;
}
if (opt_val[index] == value)
return true;
if (opt_val[index] != 0xffffU && !stm32f1_option_erase(target))
return false;
opt_val[index] = value;
const bool write16_broken = target->part_id == 0x410U && (target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M23;
for (size_t i = 0U; i < 8U; ++i) {
if (!stm32f1_option_write_erased(target, i, opt_val[i], write16_broken))
return false;
}
return true;
}
static bool stm32f1_cmd_option(target_s *target, int argc, const char **argv)
{
const uint32_t read_protected = target_mem_read32(target, FLASH_OBR) & FLASH_OBR_RDPRT;
const bool erase_requested = argc == 2 && strcmp(argv[1], "erase") == 0;
if (read_protected && !erase_requested) {
tc_printf(target, "Device is Read Protected\nUse `monitor option erase` to unprotect and erase device\n");
return true;
}
if (!stm32f1_flash_unlock(target, FLASH_BANK1_OFFSET))
return false;
target_mem_write32(target, FLASH_OPTKEYR, KEY1);
target_mem_write32(target, FLASH_OPTKEYR, KEY2);
if (erase_requested) {
if (!stm32f1_option_erase(target))
return false;
const bool write16_broken =
target->part_id == 0x410U && (target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M23;
if (!stm32f1_option_write_erased(target, 0U, stm32f1_flash_readable_key(target), write16_broken))
return false;
} else if (argc == 3) {
const uint32_t addr = strtoul(argv[1], NULL, 0);
const uint32_t val = strtoul(argv[2], NULL, 0);
if (!stm32f1_option_write(target, addr, val))
return false;
} else
tc_printf(target, "usage: monitor option erase\nusage: monitor option <addr> <value>\n");
for (size_t i = 0U; i < 16U; i += 4U) {
const uint32_t addr = FLASH_OBP_RDP + i;
const uint32_t val = target_mem_read32(target, addr);
tc_printf(target, "0x%08X: 0x%04X\n", addr, val & 0xffffU);
tc_printf(target, "0x%08X: 0x%04X\n", addr + 2U, val >> 16U);
}
return true;
}