flash/stm32l4x: switch to to c loader instead of assembly loader
[openocd.git] / contrib / loaders / flash / stm32 / stm32l4x.c
diff --git a/contrib/loaders/flash/stm32/stm32l4x.c b/contrib/loaders/flash/stm32/stm32l4x.c
new file mode 100644 (file)
index 0000000..bcad988
--- /dev/null
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/**
+ * Copyright (C) 2021 Tarek BOCHKATI
+ *   tarek.bouchkati@st.com
+ */
+
+#include <stdint.h>
+#include "../../../../src/flash/nor/stm32l4x.h"
+
+static inline __attribute__((always_inline))
+void copy_buffer_u32(uint32_t *dst, uint32_t *src, int len)
+{
+       for (int i = 0; i < len; i++)
+               dst[i] = src[i];
+}
+
+/* this function is assumes that fifo_size is multiple of flash_word_size
+ * this condition is ensured by target_run_flash_async_algorithm
+ */
+
+void write(volatile struct stm32l4_work_area *work_area,
+                  uint8_t *fifo_end,
+                  uint8_t *target_address,
+                  uint32_t count)
+{
+       volatile uint32_t *flash_sr = (uint32_t *) work_area->params.flash_sr_addr;
+       volatile uint32_t *flash_cr = (uint32_t *) work_area->params.flash_cr_addr;
+
+       /* optimization to avoid reading from memory each time */
+       uint8_t *rp_cache  = work_area->fifo.rp;
+
+       /* fifo_start is used to wrap when we reach fifo_end */
+       uint8_t *fifo_start = rp_cache;
+
+       /* enable flash programming */
+       *flash_cr = FLASH_PG;
+
+       while (count) {
+               /* optimization to avoid reading from memory each time */
+               uint8_t *wp_cache  = work_area->fifo.wp;
+               if (wp_cache == 0)
+                       break; /* aborted by target_run_flash_async_algorithm */
+
+               int32_t fifo_size = wp_cache - rp_cache;
+               if (fifo_size < 0) {
+                       /* consider the linear fifo, we will wrap later */
+                       fifo_size = fifo_end - rp_cache;
+               }
+
+               /* wait for at least a flash word */
+               while (fifo_size >= work_area->params.flash_word_size) {
+                       copy_buffer_u32((uint32_t *)target_address,
+                                       (uint32_t *)rp_cache,
+                                       work_area->params.flash_word_size / 4);
+
+                       /* update target_address and rp_cache */
+                       target_address += work_area->params.flash_word_size;
+                       rp_cache += work_area->params.flash_word_size;
+
+                       /* wait for the busy flag */
+                       while (*flash_sr & work_area->params.flash_sr_bsy_mask)
+                               ;
+
+                       if (*flash_sr & FLASH_ERROR) {
+                               work_area->fifo.rp = 0; /* set rp to zero 0 on error */
+                               goto write_end;
+                       }
+
+                       /* wrap if reach the fifo_end, and update rp in memory */
+                       if (rp_cache >= fifo_end)
+                               rp_cache = fifo_start;
+
+                       /* flush the rp cache value,
+                        * so target_run_flash_async_algorithm can fill the circular fifo */
+                       work_area->fifo.rp = rp_cache;
+
+                       /* update fifo_size and count */
+                       fifo_size -= work_area->params.flash_word_size;
+                       count--;
+               }
+       }
+
+write_end:
+       /* disable flash programming */
+       *flash_cr = 0;
+
+       /* soft break the loader */
+       __asm("bkpt 0");
+}
+
+/* by enabling this define 'DEBUG':
+ * the main() function can help help debugging the loader algo
+ * note: the application should be linked into RAM */
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+/* device selector: STM32L5 | STM32U5 | STM32WB | STM32WL | STM32WL_CPU2 | STM32G0Bx | ... */
+#define STM32U5
+
+/* when using a secure device, and want to test the secure programming enable this define */
+/* #define SECURE */
+
+#if defined(STM32U5)
+#  define FLASH_WORD_SIZE   16
+#else
+#  define FLASH_WORD_SIZE   8
+#endif
+
+#if defined(STM32WB) || defined(STM32WL)
+#  define FLASH_BASE        0x58004000
+#else
+#  define FLASH_BASE        0x40022000
+#endif
+
+#if defined(STM32G0Bx)
+#  define FLASH_BSY_MASK      (FLASH_BSY | FLASH_BSY2)
+#else
+#  define FLASH_BSY_MASK      FLASH_BSY
+#endif
+
+#if defined(STM32L5) || defined(STM32U5)
+#  ifdef SECURE
+#    define FLASH_KEYR_OFFSET 0x0c
+#    define FLASH_SR_OFFSET   0x24
+#    define FLASH_CR_OFFSET   0x2c
+#  else
+#    define FLASH_KEYR_OFFSET 0x08
+#    define FLASH_SR_OFFSET   0x20
+#    define FLASH_CR_OFFSET   0x28
+#  endif
+#elif defined(STM32WL_CPU2)
+#  define FLASH_KEYR_OFFSET 0x08
+#  define FLASH_SR_OFFSET   0x60
+#  define FLASH_CR_OFFSET   0x64
+#else
+#  define FLASH_KEYR_OFFSET 0x08
+#  define FLASH_SR_OFFSET   0x10
+#  define FLASH_CR_OFFSET   0x14
+#endif
+
+#define FLASH_KEYR        (uint32_t *)((FLASH_BASE) + (FLASH_KEYR_OFFSET))
+#define FLASH_SR          (uint32_t *)((FLASH_BASE) + (FLASH_SR_OFFSET))
+#define FLASH_CR          (uint32_t *)((FLASH_BASE) + (FLASH_CR_OFFSET))
+
+int main()
+{
+       const uint32_t count = 2;
+       const uint32_t buf_size = count * FLASH_WORD_SIZE;
+       const uint32_t work_area_size = sizeof(struct stm32l4_work_area) + buf_size;
+
+       uint8_t work_area_buf[work_area_size];
+       struct stm32l4_work_area *workarea = (struct stm32l4_work_area *)work_area_buf;
+
+       /* fill the workarea struct */
+       workarea->params.flash_sr_addr = (uint32_t)(FLASH_SR);
+       workarea->params.flash_cr_addr = (uint32_t)(FLASH_CR);
+       workarea->params.flash_word_size = FLASH_WORD_SIZE;
+       workarea->params.flash_sr_bsy_mask = FLASH_BSY_MASK;
+       /* note: the workarea->stack is not used, in this configuration */
+
+       /* programming the existing memory raw content in workarea->fifo.buf */
+       /* feel free to fill the memory with magical values ... */
+
+       workarea->fifo.wp =  (uint8_t *)(&workarea->fifo.buf + buf_size);
+       workarea->fifo.rp =  (uint8_t *)&workarea->fifo.buf;
+
+       /* unlock the flash */
+       *FLASH_KEYR = KEY1;
+       *FLASH_KEYR = KEY2;
+
+       /* erase sector 0 */
+       *FLASH_CR = FLASH_PER | FLASH_STRT;
+       while (*FLASH_SR & FLASH_BSY)
+               ;
+
+       /* flash address, should be aligned to FLASH_WORD_SIZE */
+       uint8_t *target_address = (uint8_t *) 0x8000000;
+
+       write(workarea,
+                 (uint8_t *)(workarea + work_area_size),
+                 target_address,
+                 count);
+
+       while (1)
+               ;
+}
+#endif /* DEBUG */

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)