flash/stm32l4x: add support of STM32U57x/U58x 08/6108/15
authorTarek BOCHKATI <tarek.bouchkati@gmail.com>
Tue, 16 Mar 2021 15:10:59 +0000 (16:10 +0100)
committerOleksij Rempel <linux@rempel-privat.de>
Mon, 30 Aug 2021 07:51:52 +0000 (07:51 +0000)
this device flash registers are quite similar to STM32L5
with this changes :
 - flash size is up to 2MB
 - 2MB variants are always dual bank
 - 1MB and 512KB variants could be dual bank (contiguous addressing)
   depending on DUALBANK bit(21)
 - flash data width is 16 bytes (quad-word)

Change-Id: Id13c552270ce1071479ad418526e8a39ebe83cb1
Signed-off-by: Tarek BOCHKATI <tarek.bouchkati@gmail.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/6108
Tested-by: jenkins
Reviewed-by: Oleksij Rempel <linux@rempel-privat.de>
doc/openocd.texi
src/flash/nor/stm32l4x.c
src/flash/startup.tcl
tcl/target/stm32u5x.cfg [new file with mode: 0644]

index 4404807a639c9be22a96f3fc263ca95de2c2e39c..2759a39d33041e760c6adfa3c61379fd8c14f586 100644 (file)
@@ -7302,7 +7302,7 @@ The @var{num} parameter is a value shown by @command{flash banks}.
 @end deffn
 
 @deffn {Flash Driver} {stm32l4x}
-All members of the STM32 G0, G4, L4, L4+, L5, WB and WL
+All members of the STM32 G0, G4, L4, L4+, L5, U5, WB and WL
 microcontroller families from STMicroelectronics include internal flash
 and use ARM Cortex-M0+, M4 and M33 cores.
 The driver automatically recognizes a number of these chips using
index 8d463ac0593e6f5af11118115524c6fd9fe118c2..b8635fe75ccd60de828e35757f5b5d82acc10e87 100644 (file)
 #define F_HAS_TZ            BIT(2)
 /* this flag indicates if the device has the same flash registers as STM32L5 */
 #define F_HAS_L5_FLASH_REGS BIT(3)
+/* this flag indicates that programming should be done in quad-word
+ * the default programming word size is double-word */
+#define F_QUAD_WORD_PROG    BIT(4)
 /* end of STM32L4 flags ******************************************************/
 
 
@@ -236,6 +239,7 @@ struct stm32l4_flash_bank {
        bool dual_bank_mode;
        int hole_sectors;
        uint32_t user_bank_size;
+       uint32_t data_width;
        uint32_t cr_bker_mask;
        uint32_t sr_bsy_mask;
        uint32_t wrpxxr_mask;
@@ -265,7 +269,7 @@ struct stm32l4_wrp {
 };
 
 /* human readable list of families this drivers supports (sorted alphabetically) */
-static const char *device_families = "STM32G0/G4/L4/L4+/L5/WB/WL";
+static const char *device_families = "STM32G0/G4/L4/L4+/L5/U5/WB/WL";
 
 static const struct stm32l4_rev stm32_415_revs[] = {
        { 0x1000, "1" }, { 0x1001, "2" }, { 0x1003, "3" }, { 0x1007, "4" }
@@ -323,6 +327,10 @@ static const struct stm32l4_rev stm32_479_revs[] = {
        { 0x1000, "A" },
 };
 
+static const struct stm32l4_rev stm32_482_revs[] = {
+       { 0x1000, "A" }, { 0x1001, "Z" }, { 0x1003, "Y" }, { 0x2000, "B" },
+};
+
 static const struct stm32l4_rev stm32_495_revs[] = {
        { 0x2001, "2.1" },
 };
@@ -504,6 +512,18 @@ static const struct stm32l4_part_info stm32l4_parts[] = {
          .otp_base              = 0x1FFF7000,
          .otp_size              = 1024,
        },
+       {
+         .id                    = 0x482,
+         .revs                  = stm32_482_revs,
+         .num_revs              = ARRAY_SIZE(stm32_482_revs),
+         .device_str            = "STM32U57/U58xx",
+         .max_flash_size_kb     = 2048,
+         .flags                 = F_HAS_DUAL_BANK | F_QUAD_WORD_PROG | F_HAS_TZ | F_HAS_L5_FLASH_REGS,
+         .flash_regs_base       = 0x40022000,
+         .fsize_addr            = 0x0BFA07A0,
+         .otp_base              = 0x0BFA0000,
+         .otp_size              = 512,
+       },
        {
          .id                    = 0x495,
          .revs                  = stm32_495_revs,
@@ -559,10 +579,6 @@ FLASH_BANK_COMMAND_HANDLER(stm32l4_flash_bank_command)
                return ERROR_FAIL; /* Checkme: What better error to use?*/
        bank->driver_priv = stm32l4_info;
 
-       /* The flash write must be aligned to a double word (8-bytes) boundary.
-        * Ask the flash infrastructure to ensure required alignment */
-       bank->write_start_alignment = bank->write_end_alignment = 8;
-
        stm32l4_info->probed = false;
        stm32l4_info->otp_enabled = false;
        stm32l4_info->user_bank_size = bank->size;
@@ -1297,11 +1313,12 @@ static int stm32l4_protect(struct flash_bank *bank, int set, unsigned int first,
        return stm32l4_write_all_wrpxy(bank, wrpxy, n_wrp);
 }
 
-/* Count is in double-words */
+/* count is the size divided by stm32l4_info->data_width */
 static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
        uint32_t offset, uint32_t count)
 {
        struct target *target = bank->target;
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
        uint32_t buffer_size;
        struct working_area *write_algorithm;
        struct working_area *source;
@@ -1328,7 +1345,11 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
                return retval;
        }
 
-       /* memory buffer, size *must* be multiple of dword plus one dword for rp and one for wp */
+       /* memory buffer, size *must* be multiple of stm32l4_info->data_width
+        * plus one dword for rp and one for wp */
+       /* FIXME, currently only STM32U5 devices do have a different data_width,
+        * but STM32U5 device flash programming does not go through this function
+        * so temporarily continue to consider the default data_width = 8 */
        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");
@@ -1360,7 +1381,7 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
        buf_set_u32(reg_params[4].value, 0, 32, stm32l4_get_flash_reg_by_index(bank, STM32_FLASH_SR_INDEX));
        buf_set_u32(reg_params[5].value, 0, 32, stm32l4_get_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX));
 
-       retval = target_run_flash_async_algorithm(target, buffer, count, 8,
+       retval = target_run_flash_async_algorithm(target, buffer, count, stm32l4_info->data_width,
                        0, NULL,
                        ARRAY_SIZE(reg_params), reg_params,
                        source->address, source->size,
@@ -1396,10 +1417,11 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
        return retval;
 }
 
-/* Count is in double-words */
+/* count is the size divided by stm32l4_info->data_width */
 static int stm32l4_write_block_without_loader(struct flash_bank *bank, const uint8_t *buffer,
                                uint32_t offset, uint32_t count)
 {
+       struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
        struct target *target = bank->target;
        uint32_t address = bank->base + offset;
        int retval = ERROR_OK;
@@ -1417,8 +1439,9 @@ static int stm32l4_write_block_without_loader(struct flash_bank *bank, const uin
 
        /* write directly to flash memory */
        const uint8_t *src = buffer;
+       const uint32_t data_width_in_words = stm32l4_info->data_width / 4;
        while (count--) {
-               retval = target_write_memory(target, address, 4, 2, src);
+               retval = target_write_memory(target, address, 4, data_width_in_words, src);
                if (retval != ERROR_OK)
                        return retval;
 
@@ -1427,8 +1450,8 @@ static int stm32l4_write_block_without_loader(struct flash_bank *bank, const uin
                if (retval != ERROR_OK)
                        return retval;
 
-               src += 8;
-               address += 8;
+               src += stm32l4_info->data_width;
+               address += stm32l4_info->data_width;
        }
 
        /* reset PG in FLASH_CR */
@@ -1455,10 +1478,13 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer,
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       /* The flash write must be aligned to a double word (8-bytes) boundary.
+       /* ensure that stm32l4_info->data_width is 'at least' a multiple of dword */
+       assert(stm32l4_info->data_width % 8 == 0);
+
+       /* The flash write must be aligned to the 'stm32l4_info->data_width' boundary.
         * The flash infrastructure ensures it, do just a security check */
-       assert(offset % 8 == 0);
-       assert(count % 8 == 0);
+       assert(offset % stm32l4_info->data_width == 0);
+       assert(count % stm32l4_info->data_width == 0);
 
        /* STM32G4xxx Cat. 3 devices may have gaps between banks, check whether
         * data to be written does not go into a gap:
@@ -1520,6 +1546,12 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer,
        if ((stm32l4_info->part_info->id == 0x467) && stm32l4_info->dual_bank_mode) {
                LOG_INFO("Couldn't use the flash loader in dual-bank mode");
                use_flashloader = false;
+       } else if (stm32l4_info->part_info->id == 0x482) {
+               /**
+                * FIXME the current flashloader does not support writing in quad-words
+                * which is required for STM32U5 devices.
+                */
+               use_flashloader = false;
        }
 
        if (use_flashloader) {
@@ -1530,15 +1562,16 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer,
                if (stm32l4_info->tzen && (stm32l4_info->rdp == RDP_LEVEL_0_5))
                        LOG_INFO("RDP level is 0.5, the work-area should reside in non-secure RAM");
 
-               retval = stm32l4_write_block(bank, buffer, offset, count / 8);
+               retval = stm32l4_write_block(bank, buffer, offset,
+                               count / stm32l4_info->data_width);
        }
 
        if (!use_flashloader || retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
                LOG_INFO("falling back to single memory accesses");
-               retval = stm32l4_write_block_without_loader(bank, buffer, offset, count / 8);
+               retval = stm32l4_write_block_without_loader(bank, buffer, offset,
+                               count / stm32l4_info->data_width);
        }
 
-
 err_lock:
        retval2 = stm32l4_write_flash_reg_by_index(bank, stm32l4_get_flash_cr_with_lock_index(bank), FLASH_LOCK);
 
@@ -1657,9 +1690,14 @@ static int stm32l4_probe(struct flash_bank *bank)
                        stm32l4_info->idcode, part_info->device_str, rev_str, rev_id);
 
        stm32l4_info->flash_regs_base = stm32l4_info->part_info->flash_regs_base;
+       stm32l4_info->data_width = (part_info->flags & F_QUAD_WORD_PROG) ? 16 : 8;
        stm32l4_info->cr_bker_mask = FLASH_BKER;
        stm32l4_info->sr_bsy_mask = FLASH_BSY;
 
+       /* Set flash write alignment boundaries.
+        * Ask the flash infrastructure to ensure required alignment */
+       bank->write_start_alignment = bank->write_end_alignment = stm32l4_info->data_width;
+
        /* initialise the flash registers layout */
        if (part_info->flags & F_HAS_L5_FLASH_REGS)
                stm32l4_info->flash_regs = stm32l5_ns_flash_regs;
@@ -1852,6 +1890,18 @@ static int stm32l4_probe(struct flash_bank *bank)
                        stm32l4_info->bank1_sectors = num_pages / 2;
                }
                break;
+       case 0x482: /* STM32U57/U58xx */
+               /* if flash size is max (2M) the device is always dual bank
+                * otherwise check DUALBANK bit(21)
+                */
+               page_size_kb = 8;
+               num_pages = flash_size_kb / page_size_kb;
+               stm32l4_info->bank1_sectors = num_pages;
+               if ((flash_size_kb == part_info->max_flash_size_kb) || (stm32l4_info->optr & BIT(21))) {
+                       stm32l4_info->dual_bank_mode = true;
+                       stm32l4_info->bank1_sectors = num_pages / 2;
+               }
+               break;
        case 0x495: /* STM32WB5x */
        case 0x496: /* STM32WB3x */
                /* single bank flash */
index 0a26da08bc266c54c77642a1715b28bea1ad1fd1..93ef82ce618e147807fab44f1aaf017c606d9b57 100644 (file)
@@ -114,9 +114,10 @@ proc stm32f7x args { eval stm32f2x $args }
 proc stm32l0x args { eval stm32lx $args }
 proc stm32l1x args { eval stm32lx $args }
 
-# stm32[g0|g4|wb|wl] uses the same flash driver as the stm32l4x
+# stm32[g0|g4|l5|u5|wb|wl] uses the same flash driver as the stm32l4x
 proc stm32g0x args { eval stm32l4x $args }
 proc stm32g4x args { eval stm32l4x $args }
 proc stm32l5x args { eval stm32l4x $args }
+proc stm32u5x args { eval stm32l4x $args }
 proc stm32wbx args { eval stm32l4x $args }
 proc stm32wlx args { eval stm32l4x $args }
diff --git a/tcl/target/stm32u5x.cfg b/tcl/target/stm32u5x.cfg
new file mode 100644 (file)
index 0000000..2c2c0e0
--- /dev/null
@@ -0,0 +1,207 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# script for stm32u5x family
+
+#
+# stm32u5 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 stm32u5x
+}
+
+set _ENDIAN little
+
+# Work-area is a space in RAM used for flash programming
+# By default use 64kB
+if { [info exists WORKAREASIZE] } {
+       set _WORKAREASIZE $WORKAREASIZE
+} else {
+       set _WORKAREASIZE 0x10000
+}
+
+#jtag scan chain
+if { [info exists CPUTAPID] } {
+       set _CPUTAPID $CPUTAPID
+} else {
+       if { [using_jtag] } {
+               # See STM Document RM0438
+               # RM0456 Rev1, Section 65.2.8 JTAG debug port - Table 661. JTAG-DP data registers
+               # Corresponds to Cortex®-M33 JTAG debug port ID code
+               set _CPUTAPID 0x0ba04477
+       } {
+               # SWD IDCODE (single drop, arm)
+               set _CPUTAPID 0x0be12477
+       }
+}
+
+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
+
+# use non-secure RAM by default
+$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
+
+# create sec/ns flash and otp memories (sizes will be probed)
+flash bank $_CHIPNAME.flash_ns      stm32l4x 0x08000000 0 0 0 $_TARGETNAME
+flash bank $_CHIPNAME.flash_alias_s stm32l4x 0x0C000000 0 0 0 $_TARGETNAME
+flash bank $_CHIPNAME.otp           stm32l4x 0x0BFA0000 0 0 0 $_TARGETNAME
+
+# Common knowledges tells JTAG speed should be <= F_CPU/6.
+# F_CPU after reset is MSI 4MHz, so use F_JTAG = 500 kHz to stay on
+# the safe side.
+#
+# Note that there is a pretty wide band where things are
+# more or less stable, see http://openocd.zylin.com/#/c/3366/
+adapter speed 500
+
+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 is_secure {} {
+       # read Debug Security Control and Status Regsiter (DSCSR) and check CDS (bit 16)
+       set DSCSR [mrw 0xE000EE08]
+       return [expr {($DSCSR & (1 << 16)) != 0}]
+}
+
+proc clock_config_160_mhz {} {
+       set offset [expr {[is_secure] ? 0x10000000 : 0}]
+       # MCU clock is at MSI 4MHz after reset, set MCU freq at 160 MHz with PLL
+
+       # Enable voltage range 1 for frequency above 100 Mhz
+       # RCC_AHB3ENR = PWREN
+       mww [expr {0x46020C94 + $offset}] 0x00000004
+       # delay for register clock enable (read back reg)
+       mrw [expr {0x56020C94 + $offset}]
+       # PWR_VOSR : VOS Range 1
+       mww [expr {0x4602080C + $offset}] 0x00030000
+       # delay for register write (read back reg)
+       mrw [expr {0x4602080C + $offset}]
+       # FLASH_ACR : 4 WS for 160 MHz HCLK
+       mww [expr {0x40022000 + $offset}] 0x00000004
+       # RCC_PLL1CFGR => PLL1M=0000=/1, PLL1SRC=MSI 4MHz
+       mww [expr {0x46020C28 + $offset}] 0x00000001
+       # RCC_PLL1DIVR => PLL1P=PLL1Q=PLL1R=000001=/2, PLL1N=0x4F=80
+       # fVCO = 4 x 80 /1 = 320
+       # SYSCLOCK = fVCO/PLL1R = 320/2 = 160 MHz
+       mmw [expr {0x46020C34 + $offset}] 0x0000004F 0
+       # RCC_PLL1CFGR => PLL1REN=1
+       mmw [expr {0x46020C28 + $offset}] 0x00040000 0
+       # RCC_CR |= PLL1ON
+       mmw [expr {0x46020C00 + $offset}] 0x01000000 0
+       # while !(RCC_CR & PLL1RDY)
+       while {!([mrw [expr {0x46020C00 + $offset}]] & 0x02000000)} {}
+       # RCC_CFGR1 |= SW_PLL
+       mmw [expr {0x46020C1C + $offset}] 0x00000003 0
+       # while ((RCC_CFGR1 & SWS) != PLL)
+       while {([mrw [expr {0x46020C1C + $offset}]] & 0x0C) != 0x0C} {}
+}
+
+proc ahb_ap_non_secure_access {} {
+       # SPROT=1=Non Secure access, Priv=1
+       [[target current] cget -dap] apcsw 0x4B000000 0x4F000000
+}
+
+proc ahb_ap_secure_access {} {
+       # SPROT=0=Secure access, Priv=1
+       [[target current] cget -dap] apcsw 0x0B000000 0x4F000000
+}
+
+$_TARGETNAME configure -event reset-init {
+       clock_config_160_mhz
+       # Boost JTAG frequency
+       adapter speed 4000
+}
+
+$_TARGETNAME configure -event reset-start {
+       # Reset clock is MSI (4 MHz)
+       adapter speed 480
+}
+
+$_TARGETNAME configure -event examine-end {
+       # DBGMCU_CR |= DBG_STANDBY | DBG_STOP
+       mmw 0xE0044004 0x00000006 0
+
+       # Stop watchdog counters during halt
+       # DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP
+       mmw 0xE0044008 0x00001800 0
+}
+
+$_TARGETNAME configure -event halted {
+       set secure [is_secure]
+
+       if {$secure} {
+               set secure_str "Secure"
+               ahb_ap_secure_access
+       } else {
+               set secure_str "Non-Secure"
+               ahb_ap_non_secure_access
+       }
+
+       # print the secure state only when it changes
+       set _TARGETNAME [target current]
+       global $_TARGETNAME.secure
+
+       if {![info exists $_TARGETNAME.secure] || $secure != [set $_TARGETNAME.secure]} {
+               echo "CPU in $secure_str state"
+               # update saved security state
+               set $_TARGETNAME.secure $secure
+       }
+}
+
+$_TARGETNAME configure -event gdb-flash-erase-start {
+       set use_secure_workarea 0
+       # check if FLASH_OPTR.TZEN is enabled
+       set FLASH_OPTR [mrw 0x40022040]
+       if {[expr {$FLASH_OPTR & 0x80000000}] == 0} {
+               echo "TZEN option bit disabled"
+               ahb_ap_non_secure_access
+       } else {
+               ahb_ap_secure_access
+               echo "TZEN option bit enabled"
+
+               # check if FLASH_OPTR.RDP is not Level 0.5
+               if {[expr {$FLASH_OPTR & 0xFF}] != 0x55} {
+                       set use_secure_workarea 1
+               }
+       }
+
+       set _TARGETNAME [target current]
+       set workarea_addr [$_TARGETNAME cget -work-area-phys]
+       echo "workarea_addr $workarea_addr"
+
+       if {$use_secure_workarea} {
+               set workarea_addr [expr {$workarea_addr | 0x10000000}]
+       } else {
+               set workarea_addr [expr {$workarea_addr & ~0x10000000}]
+       }
+
+       $_TARGETNAME configure -work-area-phys $workarea_addr
+}
+
+$_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 0xE0044004 0x00000020 0
+}

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)