From: Andreas Bolsch Date: Sun, 16 Dec 2018 16:30:41 +0000 (+0100) Subject: Flash driver for STM32G0xx and STM32G4xx X-Git-Tag: v0.11.0-rc1~395 X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=commitdiff_plain;h=ba131f30a0798d97729f9517c136d32f58f57571 Flash driver for STM32G0xx and STM32G4xx Flash module of STM32G0/G4 family is quite similar to the one of STM32L4, so only minor changes are required, in particular adaption of flash loader to Cortex-M0. Register addresses passed to flash loader to simplify integration of L5. Added re-probe after option byte load. Added flash size override via cfg file. WRPxxR mask now based on max. number of pages instead of fixed 0xFF, as G4 devices fill up unused bits with '1'. Sizes in stm32l4_probe changed to multiples of 1kB. Tested with Nucleo-G071RB, G030J6, Nucleo-G431RB and Nucleo-G474RE. Gap handling in G4 Cat. 3 dual bank mode tested with STM32G473RB. This handling isn't optimal as the bank size includes the size of the gap. WB not tested. Change-Id: I24df7c065afeb71c11c7e96de4aa9fdb91845593 Signed-off-by: Andreas Bolsch Reviewed-on: http://openocd.zylin.com/4807 Tested-by: jenkins Reviewed-by: Tarek BOCHKATI Reviewed-by: Tomas Vanek --- diff --git a/contrib/loaders/flash/stm32/stm32l4x.S b/contrib/loaders/flash/stm32/stm32l4x.S index e0ce3cb34c..9e5c41ebae 100644 --- a/contrib/loaders/flash/stm32/stm32l4x.S +++ b/contrib/loaders/flash/stm32/stm32l4x.S @@ -8,6 +8,9 @@ * Copyright (C) 2015 Uwe Bonnes * * bon@elektron.ikp.physik.tu-darmstadt.de * * * + * Copyright (C) 2018 Andreas Bolsch * + * andreas.bolsch@mni.thm.de * + * * * 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 * @@ -25,68 +28,79 @@ .text .syntax unified - .cpu cortex-m4 + .cpu cortex-m0 .thumb /* * Params : * r0 = workarea start, status (out) - * r1 = workarea end + * r1 = workarea end + 1 * r2 = target address * r3 = count (64bit words) - * r4 = flash base + * r4 = flash status register + * r5 = flash control register * * Clobbered: - * r5 - rp * r6/7 - temp (64-bit) - * r8 - wp, tmp */ -#define STM32_FLASH_CR_OFFSET 0x14 /* offset of CR register in FLASH struct */ -#define STM32_FLASH_SR_OFFSET 0x10 /* offset of SR register in FLASH struct */ - -#define STM32_PROG 0x1 /* PG */ +#include "../../../../src/flash/nor/stm32l4x.h" .thumb_func .global _start + _start: + mov r8, r3 /* copy dword count */ wait_fifo: - ldr r8, [r0, #0] /* read wp */ - cmp r8, #0 /* abort if wp == 0 */ - beq exit - ldr r5, [r0, #4] /* read rp */ - subs r6, r8, r5 /* number of bytes available for read in r6*/ - itt mi /* if wrapped around*/ - addmi r6, r1 /* add size of buffer */ - submi r6, r0 - cmp r6, #8 /* wait until 8 bytes are available */ - bcc wait_fifo + ldr r6, [r0, #0] /* read wp */ + cmp r6, #0 /* if wp == 0, */ + beq exit /* then abort */ + ldr r3, [r0, #4] /* read rp */ + subs r6, r6, r3 /* number of bytes available for read in r6 */ + bpl fifo_stat /* if not wrapped around, skip */ + adds r6, r6, r1 /* add end of buffer */ + subs r6, r6, r0 /* sub start of buffer */ +fifo_stat: + cmp r6, #8 /* wait until at least one dword available */ + bcc wait_fifo - ldr r6, =STM32_PROG - str r6, [r4, #STM32_FLASH_CR_OFFSET] - ldrd r6, [r5], #0x08 /* read one word from src, increment ptr */ - strd r6, [r2], #0x08 /* write one word to dst, increment ptr */ + movs r6, #FLASH_PG /* flash program enable */ + str r6, [r5] /* write to FLASH_CR, start operation */ + ldmia r3!, {r6, r7} /* read one dword from src, increment ptr */ + stmia r2!, {r6, r7} /* write one dword to dst, increment ptr */ dsb + ldr r7, =FLASH_BSY /* FLASH_BSY mask */ busy: - ldr r6, [r4, #STM32_FLASH_SR_OFFSET] - tst r6, #0x10000 /* BSY (bit16) == 1 => operation in progress */ - bne busy /* wait more... */ - tst r6, #0xfa /* PGSERR | SIZERR | PGAERR | WRPERR | PROGERR | OPERR */ - bne error /* fail... */ + ldr r6, [r4] /* get FLASH_SR register */ + tst r6, r7 /* BSY == 1 => operation in progress */ + bne busy /* if still set, wait more ... */ + movs r7, #FLASH_ERROR /* all error bits */ + tst r6, r7 /* check for any error bit */ + bne error /* fail ... */ + + cmp r3, r1 /* rp at end of buffer? */ + bcc upd_rp /* if no, then skip */ + subs r3, r3, r1 /* sub end of buffer */ + adds r3, r3, r0 /* add start of buffer */ + adds r3, r3, #8 /* skip wp and rp */ +upd_rp: + str r3, [r0, #4] /* store rp */ + mov r7, r8 /* get dword count */ + subs r7, r7, #1 /* decrement dword count */ + mov r8, r7 /* save dword count */ + beq exit /* exit if done */ + b wait_fifo + + .pool - cmp r5, r1 /* wrap rp at end of buffer */ - it cs - addcs r5, r0, #8 /* skip loader args */ - str r5, [r0, #4] /* store rp */ - subs r3, r3, #1 /* decrement dword count */ - cbz r3, exit /* loop if not done */ - b wait_fifo error: - movs r1, #0 - str r1, [r0, #4] /* set rp = 0 on error */ + movs r3, #0 + str r3, [r0, #4] /* set rp = 0 on error */ exit: - mov r0, r6 /* return status in r0 */ + mov r0, r6 /* return status in r0 */ + movs r6, #0 /* flash program disable */ + str r6, [r5] /* write to FLASH_CR */ + movs r6, #FLASH_ERROR /* all error bits */ + str r6, [r4] /* write to FLASH_CR to clear errors */ bkpt #0x00 - .pool - diff --git a/contrib/loaders/flash/stm32/stm32l4x.inc b/contrib/loaders/flash/stm32/stm32l4x.inc index 4065d14eff..df5c7edd16 100644 --- a/contrib/loaders/flash/stm32/stm32l4x.inc +++ b/contrib/loaders/flash/stm32/stm32l4x.inc @@ -1,7 +1,7 @@ /* Autogenerated with ../../../../src/helper/bin2char.sh */ -0xd0,0xf8,0x00,0x80,0xb8,0xf1,0x00,0x0f,0x20,0xd0,0x45,0x68,0xb8,0xeb,0x05,0x06, -0x44,0xbf,0x76,0x18,0x36,0x1a,0x08,0x2e,0xf2,0xd3,0x0d,0x4e,0x66,0x61,0xf5,0xe8, -0x02,0x67,0xe2,0xe8,0x02,0x67,0xbf,0xf3,0x4f,0x8f,0x26,0x69,0x16,0xf4,0x80,0x3f, -0xfb,0xd1,0x16,0xf0,0xfa,0x0f,0x07,0xd1,0x8d,0x42,0x28,0xbf,0x00,0xf1,0x08,0x05, -0x45,0x60,0x01,0x3b,0x13,0xb1,0xdb,0xe7,0x00,0x21,0x41,0x60,0x30,0x46,0x00,0xbe, -0x01,0x00,0x00,0x00, +0x98,0x46,0x06,0x68,0x00,0x2e,0x23,0xd0,0x43,0x68,0xf6,0x1a,0x01,0xd5,0x76,0x18, +0x36,0x1a,0x08,0x2e,0xf5,0xd3,0x01,0x26,0x2e,0x60,0xc0,0xcb,0xc0,0xc2,0xbf,0xf3, +0x4f,0x8f,0x09,0x4f,0x26,0x68,0x3e,0x42,0xfc,0xd1,0xfa,0x27,0x3e,0x42,0x0d,0xd1, +0x8b,0x42,0x02,0xd3,0x5b,0x1a,0x1b,0x18,0x08,0x33,0x43,0x60,0x47,0x46,0x01,0x3f, +0xb8,0x46,0x05,0xd0,0xdd,0xe7,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x23,0x43,0x60, +0x30,0x46,0x00,0x26,0x2e,0x60,0xfa,0x26,0x26,0x60,0x00,0xbe, diff --git a/doc/openocd.texi b/doc/openocd.texi index 711171a343..97c5a728da 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -6892,8 +6892,10 @@ The @var{num} parameter is a value shown by @command{flash banks}. @end deffn @deffn {Flash Driver} stm32l4x -All members of the STM32L4 and STM32WB microcontroller families from STMicroelectronics -include internal flash and use ARM Cortex-M4 cores. +All members of the STM32L4, STM32L4+, STM32WB and STM32G4 +microcontroller families from STMicroelectronics include internal flash +and use ARM Cortex-M4 cores. +Additionally this driver supports STM32G0 family with ARM Cortex-M0+ core. The driver automatically recognizes a number of these chips using the chip identification register, and autoconfigures itself. @@ -6903,7 +6905,8 @@ flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME Note that some devices have been found that have a flash size register that contains an invalid value, to workaround this issue you can override the probed value used by -the flash driver. +the flash driver. However, specifying a wrong value might lead to a completely +wrong flash layout, so this feature must be used carefully. @example flash bank $_FLASHNAME stm32l4x 0x08000000 0x40000 0 0 $_TARGETNAME @@ -6960,7 +6963,7 @@ This will effectively write protect all sectors in flash bank 1. @end deffn @deffn Command {stm32l4x option_load} num -Forces a re-load of the option byte registers. Will cause a reset of the device. +Forces a re-load of the option byte registers. Will cause a system reset of the device. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn @end deffn diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 64c4a9079c..b95b003dff 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -84,4 +84,5 @@ NORHEADERS = \ %D%/non_cfi.h \ %D%/ocl.h \ %D%/spi.h \ + %D%/stm32l4x.h \ %D%/msp432.h diff --git a/src/flash/nor/stm32l4x.c b/src/flash/nor/stm32l4x.c index abbb75b466..b6f0d714d8 100644 --- a/src/flash/nor/stm32l4x.c +++ b/src/flash/nor/stm32l4x.c @@ -28,6 +28,7 @@ #include #include #include "bits.h" +#include "stm32l4x.h" /* STM32L4xxx series for reference. * @@ -65,65 +66,43 @@ * */ -/* Erase time can be as high as 25ms, 10x this and assume it's toast... */ +/* STM32WBxxx series for reference. + * + * RM0434 (STM32WB55) + * http://www.st.com/resource/en/reference_manual/dm00318631.pdf + * + * RM0471 (STM32WB50) + * http://www.st.com/resource/en/reference_manual/dm00622834.pdf + */ -#define FLASH_ERASE_TIMEOUT 250 +/* + * STM32G0xxx series for reference. + * + * RM0444 (STM32G0x1) + * http://www.st.com/resource/en/reference_manual/dm00371828.pdf + * + * RM0454 (STM32G0x0) + * http://www.st.com/resource/en/reference_manual/dm00463896.pdf + */ -/* Flash registers offsets */ -#define STM32_FLASH_ACR 0x00 -#define STM32_FLASH_KEYR 0x08 -#define STM32_FLASH_OPTKEYR 0x0c -#define STM32_FLASH_SR 0x10 -#define STM32_FLASH_CR 0x14 -#define STM32_FLASH_OPTR 0x20 -#define STM32_FLASH_WRP1AR 0x2c -#define STM32_FLASH_WRP1BR 0x30 -#define STM32_FLASH_WRP2AR 0x4c -#define STM32_FLASH_WRP2BR 0x50 - -/* FLASH_CR register bits */ -#define FLASH_PG (1 << 0) -#define FLASH_PER (1 << 1) -#define FLASH_MER1 (1 << 2) -#define FLASH_PAGE_SHIFT 3 -#define FLASH_CR_BKER (1 << 11) -#define FLASH_MER2 (1 << 15) -#define FLASH_STRT (1 << 16) -#define FLASH_OPTSTRT (1 << 17) -#define FLASH_EOPIE (1 << 24) -#define FLASH_ERRIE (1 << 25) -#define FLASH_OBLLAUNCH (1 << 27) -#define FLASH_OPTLOCK (1 << 30) -#define FLASH_LOCK (1 << 31) - -/* FLASH_SR register bits */ -#define FLASH_BSY (1 << 16) -/* Fast programming not used => related errors not used*/ -#define FLASH_PGSERR (1 << 7) /* Programming sequence error */ -#define FLASH_SIZERR (1 << 6) /* Size error */ -#define FLASH_PGAERR (1 << 5) /* Programming alignment error */ -#define FLASH_WRPERR (1 << 4) /* Write protection error */ -#define FLASH_PROGERR (1 << 3) /* Programming error */ -#define FLASH_OPERR (1 << 1) /* Operation error */ -#define FLASH_EOP (1 << 0) /* End of operation */ -#define FLASH_ERROR (FLASH_PGSERR | FLASH_SIZERR | FLASH_PGAERR | FLASH_WRPERR | FLASH_PROGERR | FLASH_OPERR) - -/* register unlock keys */ -#define KEY1 0x45670123 -#define KEY2 0xCDEF89AB - -/* option register unlock key */ -#define OPTKEY1 0x08192A3B -#define OPTKEY2 0x4C5D6E7F - -#define RDP_LEVEL_0 0xAA -#define RDP_LEVEL_1 0xBB -#define RDP_LEVEL_2 0xCC - - -/* other registers */ -#define DBGMCU_IDCODE 0xE0042000 +/* + * STM32G4xxx series for reference. + * + * RM0440 (STM32G43x/44x/47x/48x) + * http://www.st.com/resource/en/reference_manual/dm00355726.pdf + * + * Cat. 2 devices have single bank only, page size is 2kByte. + * + * Cat. 3 devices have single and dual bank operating modes, + * Page size is 2kByte (dual mode) or 4kByte (single mode). + * + * Bank mode is controlled by bit 22 (DBANK) in option bytes register. + * Both banks are treated as a single OpenOCD bank. + */ +/* Erase time can be as high as 25ms, 10x this and assume it's toast... */ + +#define FLASH_ERASE_TIMEOUT 250 struct stm32l4_rev { const uint16_t rev; @@ -147,9 +126,14 @@ struct stm32l4_flash_bank { int bank1_sectors; bool dual_bank_mode; int hole_sectors; + uint32_t user_bank_size; + uint32_t wrpxxr_mask; const struct stm32l4_part_info *part_info; }; +/* human readable list of families this drivers supports */ +static const char *device_families = "STM32L4/L4+/WB/G4/G0"; + static const struct stm32l4_rev stm32_415_revs[] = { { 0x1000, "1" }, { 0x1001, "2" }, { 0x1003, "3" }, { 0x1007, "4" } }; @@ -158,16 +142,32 @@ static const struct stm32l4_rev stm32_435_revs[] = { { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2001, "Y" }, }; +static const struct stm32l4_rev stm32_460_revs[] = { + { 0x1000, "A/Z" } /* A and Z, no typo in RM! */, { 0x2000, "B" }, +}; + static const struct stm32l4_rev stm32_461_revs[] = { { 0x1000, "A" }, { 0x2000, "B" }, }; static const struct stm32l4_rev stm32_462_revs[] = { - { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2001, "Y" }, + { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2001, "Y" }, }; static const struct stm32l4_rev stm32_464_revs[] = { - { 0x1000, "A" }, + { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2001, "Y" }, +}; + +static const struct stm32l4_rev stm32_466_revs[] = { + { 0x1000, "A" }, { 0x1001, "Z" }, { 0x2000, "B" }, +}; + +static const struct stm32l4_rev stm32_468_revs[] = { + { 0x1000, "A" }, { 0x2000, "B" }, { 0x2001, "Z" }, +}; + +static const struct stm32l4_rev stm32_469_revs[] = { + { 0x1000, "A" }, { 0x2000, "B" }, { 0x2001, "Z" }, }; static const struct stm32l4_rev stm32_470_revs[] = { @@ -203,6 +203,16 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .fsize_addr = 0x1FFF75E0, }, + { + .id = 0x460, + .revs = stm32_460_revs, + .num_revs = ARRAY_SIZE(stm32_460_revs), + .device_str = "STM32G07/G08xx", + .max_flash_size_kb = 128, + .has_dual_bank = false, + .flash_regs_base = 0x40022000, + .fsize_addr = 0x1FFF75E0, + }, { .id = 0x461, .revs = stm32_461_revs, @@ -233,6 +243,36 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .flash_regs_base = 0x40022000, .fsize_addr = 0x1FFF75E0, }, + { + .id = 0x466, + .revs = stm32_466_revs, + .num_revs = ARRAY_SIZE(stm32_466_revs), + .device_str = "STM32G03/G04xx", + .max_flash_size_kb = 64, + .has_dual_bank = false, + .flash_regs_base = 0x40022000, + .fsize_addr = 0x1FFF75E0, + }, + { + .id = 0x468, + .revs = stm32_468_revs, + .num_revs = ARRAY_SIZE(stm32_468_revs), + .device_str = "STM32G43/G44xx", + .max_flash_size_kb = 128, + .has_dual_bank = false, + .flash_regs_base = 0x40022000, + .fsize_addr = 0x1FFF75E0, + }, + { + .id = 0x469, + .revs = stm32_469_revs, + .num_revs = ARRAY_SIZE(stm32_469_revs), + .device_str = "STM32G47/G48xx", + .max_flash_size_kb = 512, + .has_dual_bank = true, + .flash_regs_base = 0x40022000, + .fsize_addr = 0x1FFF75E0, + }, { .id = 0x470, .revs = stm32_470_revs, @@ -283,6 +323,7 @@ FLASH_BANK_COMMAND_HANDLER(stm32l4_flash_bank_command) bank->write_start_alignment = bank->write_end_alignment = 8; stm32l4_info->probed = false; + stm32l4_info->user_bank_size = bank->size; return ERROR_OK; } @@ -409,7 +450,8 @@ static int stm32l4_unlock_option_reg(struct flash_bank *bank) return ERROR_OK; } -static int stm32l4_write_option(struct flash_bank *bank, uint32_t reg_offset, uint32_t value, uint32_t mask) +static int stm32l4_write_option(struct flash_bank *bank, uint32_t reg_offset, + uint32_t value, uint32_t mask) { uint32_t optiondata; int retval, retval2; @@ -454,17 +496,23 @@ static int stm32l4_protect_check(struct flash_bank *bank) uint32_t wrp1ar, wrp1br, wrp2ar, wrp2br; stm32l4_read_flash_reg(bank, STM32_FLASH_WRP1AR, &wrp1ar); stm32l4_read_flash_reg(bank, STM32_FLASH_WRP1BR, &wrp1br); - stm32l4_read_flash_reg(bank, STM32_FLASH_WRP2AR, &wrp2ar); - stm32l4_read_flash_reg(bank, STM32_FLASH_WRP2BR, &wrp2br); - - const uint8_t wrp1a_start = wrp1ar & 0xFF; - const uint8_t wrp1a_end = (wrp1ar >> 16) & 0xFF; - const uint8_t wrp1b_start = wrp1br & 0xFF; - const uint8_t wrp1b_end = (wrp1br >> 16) & 0xFF; - const uint8_t wrp2a_start = wrp2ar & 0xFF; - const uint8_t wrp2a_end = (wrp2ar >> 16) & 0xFF; - const uint8_t wrp2b_start = wrp2br & 0xFF; - const uint8_t wrp2b_end = (wrp2br >> 16) & 0xFF; + if (stm32l4_info->part_info->has_dual_bank) { + stm32l4_read_flash_reg(bank, STM32_FLASH_WRP2AR, &wrp2ar); + stm32l4_read_flash_reg(bank, STM32_FLASH_WRP2BR, &wrp2br); + } else { + /* prevent unintialized errors */ + wrp2ar = 0; + wrp2br = 0; + } + + const uint8_t wrp1a_start = wrp1ar & stm32l4_info->wrpxxr_mask; + const uint8_t wrp1a_end = (wrp1ar >> 16) & stm32l4_info->wrpxxr_mask; + const uint8_t wrp1b_start = wrp1br & stm32l4_info->wrpxxr_mask; + const uint8_t wrp1b_end = (wrp1br >> 16) & stm32l4_info->wrpxxr_mask; + const uint8_t wrp2a_start = wrp2ar & stm32l4_info->wrpxxr_mask; + const uint8_t wrp2a_end = (wrp2ar >> 16) & stm32l4_info->wrpxxr_mask; + const uint8_t wrp2b_start = wrp2br & stm32l4_info->wrpxxr_mask; + const uint8_t wrp2b_end = (wrp2br >> 16) & stm32l4_info->wrpxxr_mask; for (int i = 0; i < bank->num_sectors; i++) { if (i < stm32l4_info->bank1_sectors) { @@ -476,6 +524,7 @@ static int stm32l4_protect_check(struct flash_bank *bank) else bank->sectors[i].is_protected = 0; } else { + assert(stm32l4_info->part_info->has_dual_bank == true); uint8_t snb; snb = i - stm32l4_info->bank1_sectors; if (((snb >= wrp2a_start) && @@ -496,8 +545,7 @@ static int stm32l4_erase(struct flash_bank *bank, int first, int last) int i; int retval, retval2; - assert(first < bank->num_sectors); - assert(last < bank->num_sectors); + assert((0 <= first) && (first <= last) && (last < bank->num_sectors)); if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); @@ -512,7 +560,7 @@ static int stm32l4_erase(struct flash_bank *bank, int first, int last) Sector Erase To erase a sector, follow the procedure below: 1. Check that no Flash memory operation is ongoing by - checking the BSY bit in the FLASH_SR register + checking the BSY bit in the FLASH_SR register 2. Set the PER bit and select the page and bank you wish to erase in the FLASH_CR register 3. Set the STRT bit in the FLASH_CR register @@ -586,15 +634,15 @@ static int stm32l4_protect(struct flash_bank *bank, int set, int first, int last /* Count is in double-words */ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer, - uint32_t offset, uint32_t count) + uint32_t offset, uint32_t count) { struct target *target = bank->target; struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; - uint32_t buffer_size = 16384; + uint32_t buffer_size; struct working_area *write_algorithm; struct working_area *source; uint32_t address = bank->base + offset; - struct reg_param reg_params[5]; + struct reg_param reg_params[6]; struct armv7m_algorithm armv7m_info; int retval = ERROR_OK; @@ -616,18 +664,19 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer, return retval; } - /* memory buffer */ - while (target_alloc_working_area_try(target, buffer_size, &source) != - ERROR_OK) { - buffer_size /= 2; - 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("large enough working area not available, can't do block memory writes"); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - } + /* memory buffer, size *must* be multiple of dword plus one dword for rp and one for wp */ + buffer_size = target_get_working_area_avail(target) & ~(2 * sizeof(uint32_t) - 1); + if (buffer_size < 256) { + LOG_WARNING("large enough working area not available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } else if (buffer_size > 16384) { + /* probably won't benefit from more than 16k ... */ + buffer_size = 16384; + } + + if (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { + LOG_ERROR("allocating working area failed"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; @@ -637,17 +686,19 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer, init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* buffer end */ init_reg_param(®_params[2], "r2", 32, PARAM_OUT); /* target address */ init_reg_param(®_params[3], "r3", 32, PARAM_OUT); /* count (double word-64bit) */ - init_reg_param(®_params[4], "r4", 32, PARAM_OUT); /* flash regs base */ + init_reg_param(®_params[4], "r4", 32, PARAM_OUT); /* flash status register */ + init_reg_param(®_params[5], "r5", 32, PARAM_OUT); /* flash control register */ buf_set_u32(reg_params[0].value, 0, 32, source->address); buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size); buf_set_u32(reg_params[2].value, 0, 32, address); buf_set_u32(reg_params[3].value, 0, 32, count); - buf_set_u32(reg_params[4].value, 0, 32, stm32l4_info->part_info->flash_regs_base); + buf_set_u32(reg_params[4].value, 0, 32, stm32l4_info->part_info->flash_regs_base + STM32_FLASH_SR); + buf_set_u32(reg_params[5].value, 0, 32, stm32l4_info->part_info->flash_regs_base + STM32_FLASH_CR); retval = target_run_flash_async_algorithm(target, buffer, count, 8, 0, NULL, - 5, reg_params, + ARRAY_SIZE(reg_params), reg_params, source->address, source->size, write_algorithm->address, 0, &armv7m_info); @@ -676,14 +727,15 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer, destroy_reg_param(®_params[2]); destroy_reg_param(®_params[3]); destroy_reg_param(®_params[4]); + destroy_reg_param(®_params[5]); return retval; } static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer, - uint32_t offset, uint32_t count) + uint32_t offset, uint32_t count) { - int retval, retval2; + int retval = ERROR_OK, retval2; if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); @@ -695,6 +747,43 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer, assert(offset % 8 == 0); assert(count % 8 == 0); + /* STM32G4xxx Cat. 3 devices may have gaps between banks, check whether + * data to be written does not go into a gap: + * suppose buffer is fully contained in bank from sector 0 to sector + * num->sectors - 1 and sectors are ordered according to offset + */ + struct flash_sector *head = &bank->sectors[0]; + struct flash_sector *tail = &bank->sectors[bank->num_sectors - 1]; + + while ((head < tail) && (offset >= (head + 1)->offset)) { + /* buffer does not intersect head nor gap behind head */ + head++; + } + + while ((head < tail) && (offset + count <= (tail - 1)->offset + (tail - 1)->size)) { + /* buffer does not intersect tail nor gap before tail */ + --tail; + } + + LOG_DEBUG("data: 0x%08" PRIx32 " - 0x%08" PRIx32 ", sectors: 0x%08" PRIx32 " - 0x%08" PRIx32, + offset, offset + count - 1, head->offset, tail->offset + tail->size - 1); + + /* Now check that there is no gap from head to tail, this should work + * even for multiple or non-symmetric gaps + */ + while (head < tail) { + if (head->offset + head->size != (head + 1)->offset) { + LOG_ERROR("write into gap from " TARGET_ADDR_FMT " to " TARGET_ADDR_FMT, + bank->base + head->offset + head->size, + bank->base + (head + 1)->offset - 1); + retval = ERROR_FLASH_DST_OUT_OF_BANK; + } + head++; + } + + if (retval != ERROR_OK) + return retval; + retval = stm32l4_unlock_reg(bank); if (retval != ERROR_OK) goto err_lock; @@ -713,9 +802,17 @@ err_lock: static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id) { - int retval = target_read_u32(bank->target, DBGMCU_IDCODE, id); - if (retval != ERROR_OK) - return retval; + int retval; + + /* try stm32l4/l4+/wb/g4 id register first, then stm32g0 id register */ + retval = target_read_u32(bank->target, DBGMCU_IDCODE_L4_G4, id); + if ((retval != ERROR_OK) || ((*id & 0xfff) == 0) || ((*id & 0xfff) == 0xfff)) { + retval = target_read_u32(bank->target, DBGMCU_IDCODE_G0, id); + if ((retval != ERROR_OK) || ((*id & 0xfff) == 0) || ((*id & 0xfff) == 0xfff)) { + LOG_ERROR("can't get device id"); + return (retval == ERROR_OK) ? ERROR_FAIL : retval; + } + } return retval; } @@ -725,13 +822,13 @@ static int stm32l4_probe(struct flash_bank *bank) struct target *target = bank->target; struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; const struct stm32l4_part_info *part_info; - uint16_t flash_size_in_kb = 0xffff; + uint16_t flash_size_kb = 0xffff; uint32_t device_id; uint32_t options; stm32l4_info->probed = false; - /* read stm32 device id register */ + /* read stm32 device id registers */ int retval = stm32l4_read_idcode(bank, &stm32l4_info->idcode); if (retval != ERROR_OK) return retval; @@ -744,7 +841,7 @@ static int stm32l4_probe(struct flash_bank *bank) } if (!stm32l4_info->part_info) { - LOG_WARNING("Cannot identify target as an STM32 L4 or WB family device."); + LOG_WARNING("Cannot identify target as an %s family device.", device_families); return ERROR_FAIL; } @@ -758,21 +855,28 @@ static int stm32l4_probe(struct flash_bank *bank) LOG_INFO("device idcode = 0x%08" PRIx32 " (%s)", stm32l4_info->idcode, device_info); /* get flash size from target. */ - retval = target_read_u16(target, part_info->fsize_addr, &flash_size_in_kb); + retval = target_read_u16(target, part_info->fsize_addr, &flash_size_kb); /* failed reading flash size or flash size invalid (early silicon), * default to max target family */ - if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0 - || flash_size_in_kb > part_info->max_flash_size_kb) { + if (retval != ERROR_OK || flash_size_kb == 0xffff || flash_size_kb == 0 + || flash_size_kb > part_info->max_flash_size_kb) { LOG_WARNING("STM32 flash size failed, probe inaccurate - assuming %dk flash", part_info->max_flash_size_kb); - flash_size_in_kb = part_info->max_flash_size_kb; + flash_size_kb = part_info->max_flash_size_kb; + } + + /* if the user sets the size manually then ignore the probed value + * this allows us to work around devices that have a invalid flash size register value */ + if (stm32l4_info->user_bank_size) { + LOG_WARNING("overriding size register by configured bank size - MAY CAUSE TROUBLE"); + flash_size_kb = stm32l4_info->user_bank_size / 1024; } - LOG_INFO("flash size = %dkbytes", flash_size_in_kb); + LOG_INFO("flash size = %dkbytes", flash_size_kb); /* did we assign a flash size? */ - assert((flash_size_in_kb != 0xffff) && flash_size_in_kb); + assert((flash_size_kb != 0xffff) && flash_size_kb); /* read flash option register */ retval = stm32l4_read_flash_reg(bank, STM32_FLASH_OPTR, &options); @@ -783,13 +887,13 @@ static int stm32l4_probe(struct flash_bank *bank) stm32l4_info->hole_sectors = 0; int num_pages = 0; - int page_size = 0; + int page_size_kb = 0; stm32l4_info->dual_bank_mode = false; switch (device_id) { - case 0x415: - case 0x461: + case 0x415: /* STM32L47/L48xx */ + case 0x461: /* STM32L49/L4Axx */ /* if flash size is max (1M) the device is always dual bank * 0x415: has variants with 512K * 0x461: has variants with 512 and 256 @@ -798,51 +902,71 @@ static int stm32l4_probe(struct flash_bank *bank) * else -> dual bank without gap * note: the page size is invariant */ - page_size = 2048; - num_pages = flash_size_in_kb / 2; + page_size_kb = 2; + num_pages = flash_size_kb / page_size_kb; stm32l4_info->bank1_sectors = num_pages; /* check DUAL_BANK bit[21] if the flash is less than 1M */ - if (flash_size_in_kb == 1024 || (options & BIT(21))) { + if (flash_size_kb == 1024 || (options & BIT(21))) { stm32l4_info->dual_bank_mode = true; stm32l4_info->bank1_sectors = num_pages / 2; } break; - case 0x435: - case 0x462: - case 0x464: + case 0x435: /* STM32L43/L44xx */ + case 0x460: /* STM32G07/G08xx */ + case 0x462: /* STM32L45/L46xx */ + case 0x464: /* STM32L41/L42xx */ + case 0x466: /* STM32G03/G04xx */ + case 0x468: /* STM32G43/G44xx */ /* single bank flash */ - page_size = 2048; - num_pages = flash_size_in_kb / 2; + page_size_kb = 2; + num_pages = flash_size_kb / page_size_kb; stm32l4_info->bank1_sectors = num_pages; break; - case 0x470: - case 0x471: + case 0x469: /* STM32G47/G48xx */ + /* STM32G47/8 can be single/dual bank: + * if DUAL_BANK = 0 -> single bank + * else -> dual bank WITH gap + */ + page_size_kb = 4; + num_pages = flash_size_kb / page_size_kb; + stm32l4_info->bank1_sectors = num_pages; + if (options & BIT(22)) { + stm32l4_info->dual_bank_mode = true; + page_size_kb = 2; + num_pages = flash_size_kb / page_size_kb; + stm32l4_info->bank1_sectors = num_pages / 2; + + /* for devices with trimmed flash, there is a gap between both banks */ + stm32l4_info->hole_sectors = + (part_info->max_flash_size_kb - flash_size_kb) / (2 * page_size_kb); + } + break; + case 0x470: /* STM32L4R/L4Sxx */ + case 0x471: /* STM32L4P5/L4Q5x */ /* STM32L4R/S can be single/dual bank: * if size = 2M check DBANK bit(22) * if size = 1M check DB1M bit(21) * STM32L4P/Q can be single/dual bank * if size = 1M check DBANK bit(22) * if size = 512K check DB512K bit(21) - * in single bank configuration the page size is 8K - * else (dual bank) the page size is 4K without gap between banks */ - page_size = 8192; - num_pages = flash_size_in_kb / 8; + page_size_kb = 8; + num_pages = flash_size_kb / page_size_kb; stm32l4_info->bank1_sectors = num_pages; - const bool use_dbank_bit = flash_size_in_kb == part_info->max_flash_size_kb; + const bool use_dbank_bit = flash_size_kb == part_info->max_flash_size_kb; if ((use_dbank_bit && (options & BIT(22))) || (!use_dbank_bit && (options & BIT(21)))) { stm32l4_info->dual_bank_mode = true; - page_size = 4096; - num_pages = flash_size_in_kb / 4; + page_size_kb = 4; + num_pages = flash_size_kb / page_size_kb; stm32l4_info->bank1_sectors = num_pages / 2; } break; - case 0x495: + case 0x495: /* STM32WB5x */ /* single bank flash */ - page_size = 4096; - num_pages = flash_size_in_kb / 4; + page_size_kb = 4; + num_pages = flash_size_kb / page_size_kb; stm32l4_info->bank1_sectors = num_pages; break; default: @@ -852,21 +976,41 @@ static int stm32l4_probe(struct flash_bank *bank) LOG_INFO("flash mode : %s-bank", stm32l4_info->dual_bank_mode ? "dual" : "single"); - const int gap_size = stm32l4_info->hole_sectors * page_size; + const int gap_size_kb = stm32l4_info->hole_sectors * page_size_kb; - if (stm32l4_info->dual_bank_mode & gap_size) { - LOG_INFO("gap detected starting from %0x08" PRIx32 " to %0x08" PRIx32, - 0x8000000 + stm32l4_info->bank1_sectors * page_size, - 0x8000000 + stm32l4_info->bank1_sectors * page_size + gap_size); + if (gap_size_kb != 0) { + LOG_INFO("gap detected from 0x%08" PRIx32 " to 0x%08" PRIx32, + STM32_FLASH_BANK_BASE + stm32l4_info->bank1_sectors + * page_size_kb * 1024, + STM32_FLASH_BANK_BASE + (stm32l4_info->bank1_sectors + * page_size_kb + gap_size_kb) * 1024 - 1); } + /* number of significant bits in WRPxxR differs per device, + * always right adjusted, on some devices non-implemented + * bits read as '0', on others as '1' ... + * notably G4 Cat. 2 implement only 6 bits, contradicting the RM + */ + + /* use *max_flash_size* instead of actual size as the trimmed versions + * certainly use the same number of bits + * max_flash_size is always power of two, so max_pages too + */ + uint32_t max_pages = stm32l4_info->part_info->max_flash_size_kb / page_size_kb; + assert((max_pages & (max_pages - 1)) == 0); + + /* in dual bank mode number of pages is doubled, but extra bit is bank selection */ + stm32l4_info->wrpxxr_mask = ((max_pages >> (stm32l4_info->dual_bank_mode ? 1 : 0)) - 1); + assert((stm32l4_info->wrpxxr_mask & 0xFFFF0000) == 0); + LOG_DEBUG("WRPxxR mask 0x%04" PRIx16, stm32l4_info->wrpxxr_mask); + if (bank->sectors) { free(bank->sectors); bank->sectors = NULL; } - bank->size = flash_size_in_kb * 1024 + gap_size; - bank->base = 0x08000000; + bank->size = (flash_size_kb + gap_size_kb) * 1024; + bank->base = STM32_FLASH_BANK_BASE; bank->num_sectors = num_pages; bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); if (bank->sectors == NULL) { @@ -875,12 +1019,12 @@ static int stm32l4_probe(struct flash_bank *bank) } for (int i = 0; i < bank->num_sectors; i++) { - bank->sectors[i].offset = i * page_size; + bank->sectors[i].offset = i * page_size_kb * 1024; /* in dual bank configuration, if there is a gap between banks * we fix up the sector offset to consider this gap */ if (i >= stm32l4_info->bank1_sectors && stm32l4_info->hole_sectors) - bank->sectors[i].offset += gap_size; - bank->sectors[i].size = page_size; + bank->sectors[i].offset += gap_size_kb * 1024; + bank->sectors[i].size = page_size_kb * 1024; bank->sectors[i].is_erased = -1; bank->sectors[i].is_protected = 1; } @@ -911,18 +1055,20 @@ static int get_stm32l4_info(struct flash_bank *bank, char *buf, int buf_size) rev_str = part_info->revs[i].str; if (rev_str != NULL) { - snprintf(buf, buf_size, "%s - Rev: %s", - part_info->device_str, rev_str); + snprintf(buf, buf_size, "%s - Rev: %s%s", + part_info->device_str, rev_str, stm32l4_info->probed ? + (stm32l4_info->dual_bank_mode ? " dual-bank" : " single-bank") : ""); return ERROR_OK; } } } - snprintf(buf, buf_size, "%s - Rev: unknown (0x%04x)", - part_info->device_str, rev_id); + snprintf(buf, buf_size, "%s - Rev: unknown (0x%04x)%s", + part_info->device_str, rev_id, stm32l4_info->probed ? + (stm32l4_info->dual_bank_mode ? " dual-bank" : " single-bank") : ""); return ERROR_OK; } else { - snprintf(buf, buf_size, "Cannot identify target as an STM32 L4 or WB device"); + snprintf(buf, buf_size, "Cannot identify target as an %s device", device_families); return ERROR_FAIL; } @@ -1073,10 +1219,19 @@ COMMAND_HANDLER(stm32l4_handle_option_load_command) if (ERROR_OK != retval) return retval; - /* Write the OBLLAUNCH bit in CR -> Cause device "POR" and option bytes reload */ - retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_OBLLAUNCH); + /* Set OBL_LAUNCH bit in CR -> system reset and option bytes reload, + * but the RMs explicitly do *NOT* list this as power-on reset cause, and: + * "Note: If the read protection is set while the debugger is still + * connected through JTAG/SWD, apply a POR (power-on reset) instead of a system reset." + */ + retval = stm32l4_write_flash_reg(bank, STM32_FLASH_CR, FLASH_OBL_LAUNCH); + + command_print(CMD, "stm32l4x option load completed. Power-on reset might be required"); + + /* Need to re-probe after change */ + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + stm32l4_info->probed = false; - command_print(CMD, "stm32l4x option load (POR) completed."); return retval; } diff --git a/src/flash/nor/stm32l4x.h b/src/flash/nor/stm32l4x.h new file mode 100644 index 0000000000..abd8010fc7 --- /dev/null +++ b/src/flash/nor/stm32l4x.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (C) 2015 by Uwe Bonnes * + * bon@elektron.ikp.physik.tu-darmstadt.de * + * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef OPENOCD_FLASH_NOR_STM32L4X +#define OPENOCD_FLASH_NOR_STM32L4X + +/* Flash registers offsets */ +#define STM32_FLASH_ACR 0x00 +#define STM32_FLASH_KEYR 0x08 +#define STM32_FLASH_OPTKEYR 0x0c +#define STM32_FLASH_SR 0x10 +#define STM32_FLASH_CR 0x14 +#define STM32_FLASH_OPTR 0x20 +#define STM32_FLASH_WRP1AR 0x2c +#define STM32_FLASH_WRP1BR 0x30 +#define STM32_FLASH_WRP2AR 0x4c +#define STM32_FLASH_WRP2BR 0x50 + +/* FLASH_CR register bits */ +#define FLASH_PG (1 << 0) +#define FLASH_PER (1 << 1) +#define FLASH_MER1 (1 << 2) +#define FLASH_PAGE_SHIFT 3 +#define FLASH_CR_BKER (1 << 11) +#define FLASH_MER2 (1 << 15) +#define FLASH_STRT (1 << 16) +#define FLASH_OPTSTRT (1 << 17) +#define FLASH_EOPIE (1 << 24) +#define FLASH_ERRIE (1 << 25) +#define FLASH_OBL_LAUNCH (1 << 27) +#define FLASH_OPTLOCK (1 << 30) +#define FLASH_LOCK (1 << 31) + +/* FLASH_SR register bits */ +#define FLASH_BSY (1 << 16) + +/* Fast programming not used => related errors not used*/ +#define FLASH_PGSERR (1 << 7) /* Programming sequence error */ +#define FLASH_SIZERR (1 << 6) /* Size error */ +#define FLASH_PGAERR (1 << 5) /* Programming alignment error */ +#define FLASH_WRPERR (1 << 4) /* Write protection error */ +#define FLASH_PROGERR (1 << 3) /* Programming error */ +#define FLASH_OPERR (1 << 1) /* Operation error */ +#define FLASH_EOP (1 << 0) /* End of operation */ +#define FLASH_ERROR (FLASH_PGSERR | FLASH_SIZERR | FLASH_PGAERR | \ + FLASH_WRPERR | FLASH_PROGERR | FLASH_OPERR) + +/* register unlock keys */ +#define KEY1 0x45670123 +#define KEY2 0xCDEF89AB + +/* option register unlock key */ +#define OPTKEY1 0x08192A3B +#define OPTKEY2 0x4C5D6E7F + +#define RDP_LEVEL_0 0xAA +#define RDP_LEVEL_1 0xBB +#define RDP_LEVEL_2 0xCC + +/* other registers */ +#define DBGMCU_IDCODE_G0 0x40015800 +#define DBGMCU_IDCODE_L4_G4 0xE0042000 +#define DBGMCU_IDCODE_L5 0xE0044000 + +#define STM32_FLASH_BANK_BASE 0x08000000 + +#endif diff --git a/tcl/target/stm32g0x.cfg b/tcl/target/stm32g0x.cfg new file mode 100644 index 0000000000..50836ea826 --- /dev/null +++ b/tcl/target/stm32g0x.cfg @@ -0,0 +1,88 @@ +# script for stm32g0x family + +# +# stm32g0 devices support SWD transports only. +# +source [find target/swj-dp.tcl] +source [find mem_helper.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME stm32g0x +} + +set _ENDIAN little + +# Work-area is a space in RAM used for flash programming +# Smallest proposed target has 8kB ram, use 4kB by default to avoid surprises +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x1000 +} + +#jtag scan chain +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + # Section 37.5.5 - corresponds to Cortex-M0+ + set _CPUTAPID 0x0bc11477 +} + +swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap + +$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 + +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME + +# reasonable default +adapter speed 2000 + +adapter srst delay 100 +if {[using_jtag]} { + jtag_ntrst_delay 100 +} + +reset_config srst_nogate + +if {![using_hla]} { + # if srst is not fitted use SYSRESETREQ to + # perform a soft reset + cortex_m reset_config sysresetreq +} + +proc stm32g0x_default_reset_start {} { + # Reset clock is HSI16 (16 MHz) + adapter speed 2000 +} + +proc stm32g0x_default_examine_end {} { + # DBGMCU_CR |= DBG_STANDBY | DBG_STOP + mmw 0x40015804 0x00000006 0 + + # Stop watchdog counters during halt + # DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP + mmw 0x40015808 0x00001800 0 +} + +proc stm32g0x_default_reset_init {} { + # Increase clock to 64 Mhz + mmw 0x40022000 0x00000002 0x00000005 ;# FLASH_ACR: Latency = 2 + mww 0x4002100C 0x30000802 ;# RCC_PLLCFGR = PLLR=/2, PLLN=8, PLLM=/1, PLLSRC=0x2 + mmw 0x40021000 0x01000000 0x00000000 ;# RCC_CR |= PLLON + mmw 0x40021008 0x00000002 0x00000005 ;# RCC_CFGR: SW=PLLRCLK + + # Boost JTAG frequency + adapter speed 4000 +} + +# Default hooks +$_TARGETNAME configure -event examine-end { stm32g0x_default_examine_end } +$_TARGETNAME configure -event reset-start { stm32g0x_default_reset_start } +$_TARGETNAME configure -event reset-init { stm32g0x_default_reset_init } diff --git a/tcl/target/stm32g4x.cfg b/tcl/target/stm32g4x.cfg new file mode 100644 index 0000000000..9f144a07ef --- /dev/null +++ b/tcl/target/stm32g4x.cfg @@ -0,0 +1,103 @@ +# script for stm32g4x family + +# +# stm32g4 devices support both JTAG and SWD transports. +# +source [find target/swj-dp.tcl] +source [find mem_helper.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME stm32g4x +} + +set _ENDIAN little + +# Work-area is a space in RAM used for flash programming +# Smallest current target has 32kB ram, use 16kB by default to avoid surprises +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x4000 +} + +#jtag scan chain +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + if { [using_jtag] } { + # See STM Document RM0440 + # Section 46.6.3 - corresponds to Cortex-M4 r0p1 + set _CPUTAPID 0x4ba00477 + } { + set _CPUTAPID 0x2ba01477 + } +} + +swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + +if {[using_jtag]} { + jtag newtap $_CHIPNAME bs -irlen 5 +} + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap + +$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 + +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME + +if { [info exists QUADSPI] && $QUADSPI } { + set a [llength [flash list]] + set _QSPINAME $_CHIPNAME.qspi + flash bank $_QSPINAME stmqspi 0x90000000 0 0 0 $_TARGETNAME 0xA0001000 +} + +# reasonable default +adapter speed 2000 + +adapter srst delay 100 +if {[using_jtag]} { + jtag_ntrst_delay 100 +} + +reset_config srst_nogate + +if {![using_hla]} { + # if srst is not fitted use SYSRESETREQ to + # perform a soft reset + cortex_m reset_config sysresetreq +} + +$_TARGETNAME configure -event reset-init { + # CPU comes out of reset with HSION | HSIRDY. + # Use HSI 16 MHz clock, compliant even with VOS == 2. + # 1 WS compliant with VOS == 2 and 16 MHz. + mmw 0x40022000 0x00000001 0x0000000E ;# FLASH_ACR: Latency = 1 + mmw 0x40021000 0x00000100 0x00000000 ;# RCC_CR |= HSION + mmw 0x40021008 0x00000001 0x00000002 ;# RCC_CFGR: SW=HSI16 +} + +$_TARGETNAME configure -event reset-start { + # Reset clock is HSI (16 MHz) + adapter speed 2000 +} + +$_TARGETNAME configure -event examine-end { + # DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP + mmw 0xE0042004 0x00000007 0 + + # Stop watchdog counters during halt + # DBGMCU_APB1_FZR1 |= DBG_IWDG_STOP | DBG_WWDG_STOP + mmw 0xE0042008 0x00001800 0 +} + +$_TARGETNAME configure -event trace-config { + # Set TRACE_IOEN; TRACE_MODE is set to async; when using sync + # change this value accordingly to configure trace pins + # assignment + mmw 0xE0042004 0x00000020 0 +}