STM32L: Added flash driver and target
authorClément Burin des Roziers <clement.burin-des-roziers@hikob.com>
Fri, 16 Sep 2011 13:55:54 +0000 (15:55 +0200)
committerØyvind Harboe <oyvind.harboe@zylin.com>
Mon, 3 Oct 2011 16:42:39 +0000 (18:42 +0200)
Added the flash driver for the STM32L family, which highly differ from the STM32F family.
Added the TCL target file for JTAG access.

contrib/loaders/flash/stm32lx.S [new file with mode: 0644]
src/flash/nor/Makefile.am
src/flash/nor/drivers.c
src/flash/nor/stm32lx.c [new file with mode: 0644]
tcl/target/stm32l.cfg [new file with mode: 0644]

diff --git a/contrib/loaders/flash/stm32lx.S b/contrib/loaders/flash/stm32lx.S
new file mode 100644 (file)
index 0000000..6e8ccb0
--- /dev/null
@@ -0,0 +1,63 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Spencer Oliver                                  *
+ *   spen@spen-soft.co.uk                                                  *
+ *                                                                         *
+ *   Copyright (C) 2011 Øyvind Harboe                                      *
+ *   oyvind.harboe@zylin.com                                               *
+ *                                                                         *
+ *   Copyright (C) 2011 Clement Burin des Roziers                          *
+ *   clement.burin-des-roziers@hikob.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     *
+ *   (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, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+
+// Build : arm-eabi-gcc -c stm32lx.S
+       .text
+       .syntax unified
+       .cpu cortex-m3
+       .thumb
+       .thumb_func
+       .global write
+
+/*
+       r0 - destination address
+       r1 - source address
+       r2 - count
+*/
+
+       // Set 0 to r3
+       movs    r3, #0
+       // Go to compare
+       b.n test_done
+
+write_word:
+       // Load one word from address in r0, increment by 4
+       ldr.w   ip, [r1], #4
+       // Store the word to address in r1, increment by 4
+       str.w   ip, [r0], #4
+       // Increment r3
+       adds    r3, #1
+
+test_done:
+       // Compare r3 and r2
+       cmp     r3, r2
+       // Loop if not zero
+       bcc.n   write_word
+
+       // Set breakpoint to exit
+       bkpt    #0x00
+
index a9668262caa6b3a1a7d5b031797373a1c8f598ad..d5832ca275bb70d4554364bfc7d6a4f071806c7e 100644 (file)
@@ -26,6 +26,7 @@ NOR_DRIVERS = \
        stellaris.c \
        stm32f1x.c \
        stm32f2x.c \
+       stm32lx.c \
        str7x.c \
        str9x.c \
        str9xpec.c \
index a437d84e4715839055f5aac6202a37d156b9aa83..6b0cc369fa70e1d9893f82dcc6cadc1388f2e199 100644 (file)
@@ -34,6 +34,7 @@ extern struct flash_driver stellaris_flash;
 extern struct flash_driver str9xpec_flash;
 extern struct flash_driver stm32f1x_flash;
 extern struct flash_driver stm32f2x_flash;
+extern struct flash_driver stm32lx_flash;
 extern struct flash_driver tms470_flash;
 extern struct flash_driver ecosflash_flash;
 extern struct flash_driver ocl_flash;
@@ -65,6 +66,7 @@ static struct flash_driver *flash_drivers[] = {
        &str9xpec_flash,
        &stm32f1x_flash,
        &stm32f2x_flash,
+       &stm32lx_flash,
        &tms470_flash,
        &ecosflash_flash,
        &ocl_flash,
diff --git a/src/flash/nor/stm32lx.c b/src/flash/nor/stm32lx.c
new file mode 100644 (file)
index 0000000..32b3315
--- /dev/null
@@ -0,0 +1,970 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Dominic Rath                                    *
+ *   Dominic.Rath@gmx.de                                                   *
+ *                                                                         *
+ *   Copyright (C) 2008 by Spencer Oliver                                  *
+ *   spen@spen-soft.co.uk                                                  *
+ *                                                                         *
+ *   Copyright (C) 2011 by Clement Burin des Roziers                       *
+ *   clement.burin-des-roziers@hikob.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     *
+ *   (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, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <helper/binarybuffer.h>
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+
+/* stm32lx flash register locations */
+
+#define FLASH_BASE             0x40023C00
+#define FLASH_ACR              0x40023C00
+#define FLASH_PECR             0x40023C04
+#define FLASH_PDKEYR   0x40023C08
+#define FLASH_PEKEYR   0x40023C0C
+#define FLASH_PRGKEYR  0x40023C10
+#define FLASH_OPTKEYR  0x40023C14
+#define FLASH_SR               0x40023C18
+#define FLASH_OBR              0x40023C1C
+#define FLASH_WRPR             0x40023C20
+
+/* FLASH_ACR bites */
+#define FLASH_ACR__LATENCY             (1<<0)
+#define FLASH_ACR__PRFTEN              (1<<1)
+#define FLASH_ACR__ACC64               (1<<2)
+#define FLASH_ACR__SLEEP_PD            (1<<3)
+#define FLASH_ACR__RUN_PD              (1<<4)
+
+/* FLASH_PECR bits */
+#define FLASH_PECR__PELOCK             (1<<0)
+#define FLASH_PECR__PRGLOCK            (1<<1)
+#define FLASH_PECR__OPTLOCK            (1<<2)
+#define FLASH_PECR__PROG               (1<<3)
+#define FLASH_PECR__DATA               (1<<4)
+#define FLASH_PECR__FTDW               (1<<8)
+#define FLASH_PECR__ERASE              (1<<9)
+#define FLASH_PECR__FPRG               (1<<10)
+#define FLASH_PECR__EOPIE              (1<<16)
+#define FLASH_PECR__ERRIE              (1<<17)
+#define FLASH_PECR__OBL_LAUNCH (1<<18)
+
+/* FLASH_SR bits */
+#define FLASH_SR__BSY          (1<<0)
+#define FLASH_SR__EOP          (1<<1)
+#define FLASH_SR__ENDHV                (1<<2)
+#define FLASH_SR__READY                (1<<3)
+#define FLASH_SR__WRPERR       (1<<8)
+#define FLASH_SR__PGAERR       (1<<9)
+#define FLASH_SR__SIZERR       (1<<10)
+#define FLASH_SR__OPTVERR      (1<<11)
+
+/* Unlock keys */
+#define PEKEY1                 0x89ABCDEF
+#define PEKEY2                 0x02030405
+#define PRGKEY1                        0x8C9DAEBF
+#define PRGKEY2                        0x13141516
+#define OPTKEY1                        0xFBEAD9C8
+#define OPTKEY2                        0x24252627
+
+/* other registers */
+#define DBGMCU_IDCODE  0xE0042000
+#define F_SIZE                 0x1FF8004C
+
+/* Constants */
+#define FLASH_PAGE_SIZE 256
+#define FLASH_SECTOR_SIZE 4096
+#define FLASH_PAGES_PER_SECTOR 16
+#define FLASH_BANK0_ADDRESS 0x08000000
+
+/* stm32lx option byte register location */
+#define OB_RDP                 0x1FF80000
+#define OB_USER                        0x1FF80004
+#define OB_WRP0_1              0x1FF80008
+#define OB_WRP2_3              0x1FF8000C
+
+/* OB_RDP values */
+#define OB_RDP__LEVEL0 0xFF5500AA
+#define OB_RDP__LEVEL1 0xFFFF0000
+
+/* stm32lx RCC register locations */
+#define RCC_CR         0x40023800
+#define RCC_ICSCR      0x40023804
+#define RCC_CFGR       0x40023808
+
+/* RCC_ICSCR bits */
+#define RCC_ICSCR__MSIRANGE_MASK       (7<<13)
+
+static int stm32lx_unlock_program_memory(struct flash_bank *bank);
+static int stm32lx_lock_program_memory(struct flash_bank *bank);
+static int stm32lx_enable_write_half_page(struct flash_bank *bank);
+static int stm32lx_erase_sector(struct flash_bank *bank, int sector);
+static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank);
+
+struct stm32lx_flash_bank
+{
+       struct working_area *write_algorithm;
+       int probed;
+};
+
+/* flash bank stm32lx <base> <size> 0 0 <target#>
+ */
+FLASH_BANK_COMMAND_HANDLER(stm32lx_flash_bank_command)
+{
+       struct stm32lx_flash_bank *stm32lx_info;
+       if (CMD_ARGC < 6)
+       {
+               LOG_ERROR("incomplete flash_bank stm32lx configuration");
+               return ERROR_FLASH_BANK_INVALID;
+       }
+
+       // Create the bank structure
+       stm32lx_info = malloc(sizeof(struct stm32lx_flash_bank));
+
+       // Check allocation
+       if (stm32lx_info == NULL)
+       {
+               LOG_ERROR("failed to allocate bank structure");
+               return ERROR_FAIL;
+       }
+
+       bank->driver_priv = stm32lx_info;
+
+       stm32lx_info->write_algorithm = NULL;
+       stm32lx_info->probed = 0;
+
+       return ERROR_OK;
+}
+
+static int stm32lx_protect_check(struct flash_bank *bank)
+{
+       int retval;
+       struct target *target = bank->target;
+
+       uint32_t wrpr;
+
+       if (target->state != TARGET_HALTED)
+       {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /*
+        * Read the WRPR word, and check each bit (corresponding to each
+        * flash sector
+        */
+       retval = target_read_u32(target, FLASH_WRPR, &wrpr);
+       if (retval != ERROR_OK)
+               return retval;
+
+       for (int i = 0; i < 32; i++)
+       {
+               if (wrpr & (1 << i))
+               {
+                       bank->sectors[i].is_protected = 1;
+               }
+               else
+               {
+                       bank->sectors[i].is_protected = 0;
+               }
+       }
+       return ERROR_OK;
+}
+
+static int stm32lx_erase(struct flash_bank *bank, int first, int last)
+{
+       int retval;
+
+       /*
+        * It could be possible to do a mass erase if all sectors must be
+        * erased, but it is not implemented yet.
+        */
+
+       if (bank->target->state != TARGET_HALTED)
+       {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /*
+        * Loop over the selected sectors and erase them
+        */
+       for (int i = first; i <= last; i++)
+       {
+               retval = stm32lx_erase_sector(bank, i);
+               if (retval != ERROR_OK)
+                       return retval;
+               bank->sectors[i].is_erased = 1;
+       }
+       return ERROR_OK;
+}
+
+static int stm32lx_protect(struct flash_bank *bank, int set, int first,
+               int last)
+{
+       LOG_WARNING("protection of the STM32L flash is not implemented");
+       return ERROR_OK;
+}
+
+static int stm32lx_write_half_pages(struct flash_bank *bank, uint8_t *buffer,
+               uint32_t offset, uint32_t count)
+{
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+       struct target *target = bank->target;
+       uint32_t buffer_size = 4096 * 4;
+       struct working_area *source;
+       uint32_t address = bank->base + offset;
+
+       struct reg_param reg_params[5];
+       struct armv7m_algorithm armv7m_info;
+
+       int retval = ERROR_OK;
+       uint32_t reg32;
+
+       /* see contib/loaders/flash/stm32lx.s for src */
+
+       static const uint16_t stm32lx_flash_write_code_16[] =
+       {
+       //      00000000 <write_word-0x4>:
+                       0x2300, // 0:   2300            movs    r3, #0
+                       0xe004, // 2:   e004            b.n     e <test_done>
+
+                       //      00000004 <write_word>:
+                       0xf851, 0xcb04, // 4:   f851 cb04       ldr.w   ip, [r1], #4
+                       0xf840, 0xcb04, // 8:   f840 cb04       str.w   ip, [r0], #4
+                       0x3301, // c:   3301            adds    r3, #1
+
+                       //      0000000e <test_done>:
+                       0x4293, // e:   4293            cmp     r3, r2
+                       0xd3f8, // 10:  d3f8            bcc.n   4 <write_word>
+                       0xbe00, // 12:  be00            bkpt    0x0000
+
+                       };
+
+       // Flip endian
+       uint8_t stm32lx_flash_write_code[sizeof(stm32lx_flash_write_code_16)];
+       for (unsigned int i = 0; i < sizeof(stm32lx_flash_write_code_16) / 2; i++)
+       {
+               stm32lx_flash_write_code[i * 2 + 0] = stm32lx_flash_write_code_16[i]
+                               & 0xff;
+               stm32lx_flash_write_code[i * 2 + 1] = (stm32lx_flash_write_code_16[i]
+                               >> 8) & 0xff;
+       }
+       // Check if there is an even number of half pages (128bytes)
+       if (count % 128)
+       {
+               LOG_ERROR("there should be an even number "
+                               "of half pages = 128 bytes (count = %" PRIi32 " bytes)", count);
+               return ERROR_FAIL;
+       }
+
+       // Allocate working area
+       reg32 = sizeof(stm32lx_flash_write_code);
+       // Add bytes to make 4byte aligned
+       reg32 += (4 - (reg32 % 4)) % 4;
+       retval = target_alloc_working_area(target, reg32,
+                       &stm32lx_info->write_algorithm);
+       if (retval != ERROR_OK)
+               return retval;
+
+       // Write the flashing code
+       retval = target_write_buffer(target,
+                       stm32lx_info->write_algorithm->address,
+                       sizeof(stm32lx_flash_write_code),
+                       (uint8_t*) stm32lx_flash_write_code);
+       if (retval != ERROR_OK)
+       {
+               target_free_working_area(target, stm32lx_info->write_algorithm);
+               return retval;
+       }
+
+       // Allocate half pages memory
+       while (target_alloc_working_area_try(target, buffer_size, &source)
+                       != ERROR_OK)
+       {
+               if (buffer_size > 1024)
+                       buffer_size -= 1024;
+               else
+                       buffer_size /= 2;
+
+               if (buffer_size <= 256)
+               {
+                       /* if we already allocated the writing code, but failed to get a
+                        * buffer, free the algorithm */
+                       if (stm32lx_info->write_algorithm)
+                               target_free_working_area(target, stm32lx_info->write_algorithm);
+
+                       LOG_WARNING("no large enough working area available, can't do block memory writes");
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+       }
+       LOG_DEBUG("allocated working area for data (%" PRIx32 " bytes)", buffer_size);
+
+       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);
+       init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);
+
+       // Enable half-page write
+       retval = stm32lx_enable_write_half_page(bank);
+       if (retval != ERROR_OK)
+       {
+
+               target_free_working_area(target, source);
+               target_free_working_area(target, stm32lx_info->write_algorithm);
+
+               destroy_reg_param(&reg_params[0]);
+               destroy_reg_param(&reg_params[1]);
+               destroy_reg_param(&reg_params[2]);
+               destroy_reg_param(&reg_params[3]);
+
+               return retval;
+       }
+
+       // Loop while there are bytes to write
+       while (count > 0)
+       {
+               uint32_t this_count;
+               this_count = (count > buffer_size) ? buffer_size : count;
+
+               // Write the next half pages
+               retval = target_write_buffer(target, source->address, this_count,
+                               buffer);
+               if (retval != ERROR_OK)
+                       break;
+
+               // 4: Store useful information in the registers
+               // the destination address of the copy (R0)
+               buf_set_u32(reg_params[0].value, 0, 32, address);
+               // The source address of the copy (R1)
+               buf_set_u32(reg_params[1].value, 0, 32, source->address);
+               // The length of the copy (R2)
+               buf_set_u32(reg_params[2].value, 0, 32, this_count / 4);
+
+               // 5: Execute the bunch of code
+               retval = target_run_algorithm(target, 0, NULL, sizeof(reg_params)
+                               / sizeof(*reg_params), reg_params,
+                               stm32lx_info->write_algorithm->address, 0, 20000, &armv7m_info);
+               if (retval != ERROR_OK)
+                       break;
+
+               // 6: Wait while busy
+               retval = stm32lx_wait_until_bsy_clear(bank);
+               if (retval != ERROR_OK)
+                       break;
+
+               buffer += this_count;
+               address += this_count;
+               count -= this_count;
+       }
+
+       if (retval == ERROR_OK)
+               retval = stm32lx_lock_program_memory(bank);
+
+       target_free_working_area(target, source);
+       target_free_working_area(target, stm32lx_info->write_algorithm);
+
+       destroy_reg_param(&reg_params[0]);
+       destroy_reg_param(&reg_params[1]);
+       destroy_reg_param(&reg_params[2]);
+       destroy_reg_param(&reg_params[3]);
+
+       return retval;
+}
+static int stm32lx_write(struct flash_bank *bank, uint8_t *buffer,
+               uint32_t offset, uint32_t count)
+{
+       struct target *target = bank->target;
+
+       uint32_t halfpages_number;
+       uint32_t words_remaining;
+       uint32_t bytes_remaining;
+       uint32_t address = bank->base + offset;
+       uint32_t bytes_written = 0;
+       int retval;
+
+       if (bank->target->state != TARGET_HALTED)
+       {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (offset & 0x1)
+       {
+               LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
+               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+       }
+
+       // Check if there are some full half pages
+       if (((offset % 128) == 0) && (count >= 128))
+       {
+               halfpages_number = count / 128;
+               words_remaining = (count - 128 * halfpages_number) / 4;
+               bytes_remaining = (count & 0x3);
+       }
+       else
+       {
+               halfpages_number = 0;
+               words_remaining = (count / 4);
+               bytes_remaining = (count & 0x3);
+       }
+
+       if (halfpages_number)
+       {
+               retval = stm32lx_write_half_pages(bank, buffer, offset, 128
+                               * halfpages_number);
+               if (retval != ERROR_OK)
+                       return ERROR_FAIL;
+       }
+
+       bytes_written = 128 * halfpages_number;
+
+       retval = stm32lx_unlock_program_memory(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       while (words_remaining > 0)
+       {
+               uint32_t value;
+               uint8_t* p = buffer + bytes_written;
+
+               // Prepare the word, Little endian conversion
+               value = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24);
+
+               retval = target_write_u32(target, address, value);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               bytes_written += 4;
+               words_remaining--;
+               address += 4;
+
+               retval = stm32lx_wait_until_bsy_clear(bank);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       if (bytes_remaining)
+       {
+               uint32_t value = 0;
+               for (int i = 0; i < 4; i++)
+               {
+                       if (bytes_remaining)
+                       {
+                               value += (buffer[i] << (8 * i));
+                               bytes_remaining--;
+                       }
+               }
+
+               retval = target_write_u32(target, address, value);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               retval = stm32lx_wait_until_bsy_clear(bank);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       retval = stm32lx_lock_program_memory(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ERROR_OK;
+}
+
+static int stm32lx_probe(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+       int i;
+       uint16_t flash_size;
+       uint32_t device_id;
+       uint32_t reg32;
+
+       stm32lx_info->probed = 0;
+
+       /* read stm32 device id register */
+       int retval = target_read_u32(target, DBGMCU_IDCODE, &device_id);
+       if (retval != ERROR_OK)
+               return retval;
+
+       LOG_DEBUG("device id = 0x%08" PRIx32 "", device_id);
+
+       if ((device_id & 0x7ff) != 0x416)
+       {
+               LOG_WARNING("Cannot identify target as a STM32L family.");
+               return ERROR_FAIL;
+       }
+
+       // Read the RDP byte and check if it is 0xAA
+       uint8_t rdp;
+       retval = target_read_u32(target, FLASH_OBR, &reg32);
+       if (retval != ERROR_OK)
+               return retval;
+       rdp = reg32 & 0xFF;
+       if (rdp != 0xAA)
+       {
+               /*
+                * Unlocking the option byte is done by unlocking the PECR, then
+                * by writing the 2 option byte keys to OPTKEYR
+                */
+
+               /* To unlock the PECR write the 2 PEKEY to the PEKEYR register */
+               retval = target_write_u32(target, FLASH_PEKEYR, PEKEY1);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               retval = target_write_u32(target, FLASH_PEKEYR, PEKEY2);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               /* Make sure it worked */
+               retval = target_read_u32(target, FLASH_PECR, &reg32);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               if (reg32 & FLASH_PECR__PELOCK)
+                       return ERROR_FLASH_OPERATION_FAILED;
+
+               retval = target_write_u32(target, FLASH_OPTKEYR, OPTKEY1);
+               if (retval != ERROR_OK)
+                       return retval;
+               retval = target_write_u32(target, FLASH_OPTKEYR, OPTKEY2);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               retval = target_read_u32(target, FLASH_PECR, &reg32);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               if (reg32 & FLASH_PECR__OPTLOCK)
+               {
+                       LOG_ERROR("OPTLOCK is not cleared");
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+
+               // Then, write RDP to 0x00 to set level 1
+               reg32 = ((~0xAA) << 16) | (0xAA);
+               retval = target_write_u32(target, OB_RDP, reg32);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               // Set Automatic update of the option byte, by setting OBL_LAUNCH in FLASH_PECR
+               reg32 = FLASH_PECR__OBL_LAUNCH;
+               retval = target_write_u32(target, FLASH_PECR, reg32);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       /* get flash size from target. */
+       retval = target_read_u16(target, F_SIZE, &flash_size);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* check for valid flash size */
+       if (flash_size == 0xffff)
+       {
+               /* number of sectors incorrect on revA */
+               LOG_ERROR("STM32 flash size failed, probe inaccurate");
+               return ERROR_FAIL;
+       }
+
+       /* STM32L - we have 32 sectors, 16 pages per sector -> 512 pages
+        * 16 pages for a protection area */
+
+       /* calculate numbers of sectors (4kB per sector) */
+       int num_sectors = (flash_size * 1024) / FLASH_SECTOR_SIZE;
+       LOG_INFO("flash size = %dkbytes", flash_size);
+
+       if (bank->sectors)
+       {
+               free(bank->sectors);
+               bank->sectors = NULL;
+       }
+
+       bank->base = FLASH_BANK0_ADDRESS;
+       bank->size = flash_size * 1024;
+       bank->num_sectors = num_sectors;
+       bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
+       if (bank->sectors == NULL)
+       {
+               LOG_ERROR("failed to allocate bank sectors");
+               return ERROR_FAIL;
+       }
+
+       for (i = 0; i < num_sectors; i++)
+       {
+               bank->sectors[i].offset = i * FLASH_SECTOR_SIZE;
+               bank->sectors[i].size = FLASH_SECTOR_SIZE;
+               bank->sectors[i].is_erased = -1;
+               bank->sectors[i].is_protected = 1;
+       }
+
+       stm32lx_info->probed = 1;
+
+       return ERROR_OK;
+}
+
+static int stm32lx_auto_probe(struct flash_bank *bank)
+{
+       struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+
+       if (stm32lx_info->probed)
+       {
+               return ERROR_OK;
+       }
+
+       return stm32lx_probe(bank);
+}
+
+static int stm32lx_erase_check(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       const int buffer_size = 4096;
+       int i;
+       uint32_t nBytes;
+       int retval = ERROR_OK;
+
+       if (bank->target->state != TARGET_HALTED)
+       {
+               LOG_ERROR("Target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       uint8_t *buffer = malloc(buffer_size);
+       if (buffer == NULL)
+       {
+               LOG_ERROR("failed to allocate read buffer");
+               return ERROR_FAIL;
+       }
+
+       for (i = 0; i < bank->num_sectors; i++)
+       {
+               uint32_t j;
+               bank->sectors[i].is_erased = 1;
+
+               // Loop chunk by chunk over the sector
+               for (j = 0; j < bank->sectors[i].size; j += buffer_size)
+               {
+                       uint32_t chunk;
+                       chunk = buffer_size;
+                       if (chunk > (j - bank->sectors[i].size))
+                       {
+                               chunk = (j - bank->sectors[i].size);
+                       }
+
+                       retval = target_read_memory(target, bank->base
+                                       + bank->sectors[i].offset + j, 4, chunk / 4, buffer);
+                       if (retval != ERROR_OK)
+                               break;
+
+                       for (nBytes = 0; nBytes < chunk; nBytes++)
+                       {
+                               if (buffer[nBytes] != 0x00)
+                               {
+                                       bank->sectors[i].is_erased = 0;
+                                       break;
+                               }
+                       }
+               }
+               if (retval != ERROR_OK)
+               {
+                       break;
+               }
+       }
+       free(buffer);
+
+       return retval;
+}
+static int stm32lx_get_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+       // This method must return a string displaying information about the bank
+
+       struct target *target = bank->target;
+       uint32_t device_id;
+       int printed;
+
+       /* read stm32 device id register */
+       int retval = target_read_u32(target, DBGMCU_IDCODE, &device_id);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if ((device_id & 0x7ff) == 0x416)
+       {
+               printed = snprintf(buf, buf_size, "stm32lx - Rev: ");
+               buf += printed;
+               buf_size -= printed;
+
+               switch (device_id >> 16)
+               {
+                       case 0x1000:
+                               snprintf(buf, buf_size, "A");
+                               break;
+
+                       case 0x1008:
+                               snprintf(buf, buf_size, "Y");
+                               break;
+                       default:
+                               snprintf(buf, buf_size, "unknown");
+                               break;
+               }
+       }
+       else
+       {
+               snprintf(buf, buf_size, "Cannot identify target as a stm32lx");
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+static const struct command_registration stm32lx_exec_command_handlers[] =
+{
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration stm32lx_command_handlers[] =
+{
+       {
+               .name = "stm32lx",
+               .mode = COMMAND_ANY,
+               .help = "stm32lx flash command group",
+               .chain = stm32lx_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver stm32lx_flash =
+{
+               .name = "stm32lx",
+               .commands = stm32lx_command_handlers,
+               .flash_bank_command = stm32lx_flash_bank_command,
+               .erase = stm32lx_erase,
+               .protect = stm32lx_protect,
+               .write = stm32lx_write,
+               .read = default_flash_read,
+               .probe = stm32lx_probe,
+               .auto_probe = stm32lx_auto_probe,
+               .erase_check = stm32lx_erase_check,
+               .protect_check = stm32lx_protect_check,
+               .info = stm32lx_get_info,
+};
+
+// Static methods implementation
+
+static int stm32lx_unlock_program_memory(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       int retval;
+       uint32_t reg32;
+
+       /*
+        * Unlocking the program memory is done by unlocking the PECR,
+        * then by writing the 2 PRGKEY to the PRGKEYR register
+        */
+
+       /* To unlock the PECR write the 2 PEKEY to the PEKEYR register */
+       retval = target_write_u32(target, FLASH_PEKEYR, PEKEY1);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_write_u32(target, FLASH_PEKEYR, PEKEY2);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Make sure it worked */
+       retval = target_read_u32(target, FLASH_PECR, &reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (reg32 & FLASH_PECR__PELOCK)
+       {
+               LOG_ERROR("PELOCK is not cleared :(");
+               return ERROR_FLASH_OPERATION_FAILED;
+       }
+
+       retval = target_write_u32(target, FLASH_PRGKEYR, PRGKEY1);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = target_write_u32(target, FLASH_PRGKEYR, PRGKEY2);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Make sure it worked */
+       retval = target_read_u32(target, FLASH_PECR, &reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (reg32 & FLASH_PECR__PRGLOCK)
+       {
+               LOG_ERROR("PRGLOCK is not cleared :(");
+               return ERROR_FLASH_OPERATION_FAILED;
+       }
+       return ERROR_OK;
+}
+
+static int stm32lx_enable_write_half_page(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       int retval;
+       uint32_t reg32;
+
+       /**
+        * Unlock the program memory, then set the FPRG bit in the PECR register.
+        */
+       retval = stm32lx_unlock_program_memory(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_read_u32(target, FLASH_PECR, &reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       reg32 |= FLASH_PECR__FPRG;
+       retval = target_write_u32(target, FLASH_PECR, reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_read_u32(target, FLASH_PECR, &reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       reg32 |= FLASH_PECR__PROG;
+       retval = target_write_u32(target, FLASH_PECR, reg32);
+
+       return retval;
+}
+
+static int stm32lx_lock_program_memory(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       int retval;
+       uint32_t reg32;
+
+       /* To lock the program memory, simply set the lock bit and lock PECR */
+
+       retval = target_read_u32(target, FLASH_PECR, &reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       reg32 |= FLASH_PECR__PRGLOCK;
+       retval = target_write_u32(target, FLASH_PECR, reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_read_u32(target, FLASH_PECR, &reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       reg32 |= FLASH_PECR__PELOCK;
+       retval = target_write_u32(target, FLASH_PECR, reg32);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ERROR_OK;
+}
+
+static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
+{
+       struct target *target = bank->target;
+       int retval;
+       uint32_t reg32;
+
+       /*
+        * To erase a sector (i.e. FLASH_PAGES_PER_SECTOR pages),
+        * first unlock the memory, loop over the pages of this sector
+        * and write 0x0 to its first word.
+        */
+
+       retval = stm32lx_unlock_program_memory(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       for (int page = 0; page < FLASH_PAGES_PER_SECTOR; page++)
+       {
+               reg32 = FLASH_PECR__PROG | FLASH_PECR__ERASE;
+               retval = target_write_u32(target, FLASH_PECR, reg32);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               retval = stm32lx_wait_until_bsy_clear(bank);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               uint32_t addr = bank->base + bank->sectors[sector].offset + (page
+                               * FLASH_PAGE_SIZE);
+               retval = target_write_u32(target, addr, 0x0);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               retval = stm32lx_wait_until_bsy_clear(bank);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       retval = stm32lx_lock_program_memory(bank);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return ERROR_OK;
+}
+
+static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank)
+{
+       struct target *target = bank->target;
+       uint32_t status;
+       int retval = ERROR_OK;
+       int timeout = 100;
+
+       /* wait for busy to clear */
+       for (;;)
+       {
+               retval = target_read_u32(target, FLASH_SR, &status);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               if ((status & FLASH_SR__BSY) == 0)
+               {
+                       break;
+               }
+               if (timeout-- <= 0)
+               {
+                       LOG_ERROR("timed out waiting for flash");
+                       return ERROR_FAIL;
+               }
+               alive_sleep(1);
+       }
+
+       if (status & FLASH_SR__WRPERR)
+       {
+               LOG_ERROR("access denied / write protected");
+               retval = ERROR_FAIL;
+       }
+
+       if (status & FLASH_SR__PGAERR)
+       {
+               LOG_ERROR("invalid program address");
+               retval = ERROR_FAIL;
+       }
+
+       return retval;
+}
diff --git a/tcl/target/stm32l.cfg b/tcl/target/stm32l.cfg
new file mode 100644 (file)
index 0000000..5c3d368
--- /dev/null
@@ -0,0 +1,81 @@
+# script for stm32l
+
+if { [info exists CHIPNAME] } {
+   set  _CHIPNAME $CHIPNAME
+} else {
+   set  _CHIPNAME stm32l
+}
+
+if { [info exists ENDIAN] } {
+   set  _ENDIAN $ENDIAN
+} else {
+   set  _ENDIAN little
+}
+
+# Work-area is a space in RAM used for flash programming
+# By default use 14kB
+if { [info exists WORKAREASIZE] } {
+   set  _WORKAREASIZE $WORKAREASIZE
+} else {
+   set  _WORKAREASIZE 0x3800
+}
+
+# JTAG speed should be <= F_CPU/6.
+# F_CPU after reset is 2MHz, so use F_JTAG max = 333kHz
+adapter_khz 100
+
+adapter_nsrst_delay 100
+jtag_ntrst_delay 100
+
+#jtag scan chain
+if { [info exists CPUTAPID ] } {
+   set _CPUTAPID $CPUTAPID
+} else {
+  # See STM Document RM0038
+  # Section 24.6.3
+   set _CPUTAPID 0x4ba00477
+}
+jtag newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID
+
+if { [info exists BSTAPID ] } {
+   # FIXME this never gets used to override defaults...
+   set _BSTAPID $BSTAPID
+} else {
+  # See STM Document RM0038
+  # Section 24.6.2
+  set _BSTAPID 0x06416041
+}
+jtag newtap $_CHIPNAME bs -irlen 5 -expected-id $_BSTAPID
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME cortex_m3 -endian $_ENDIAN -chain-position $_TARGETNAME
+
+$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
+
+
+# flash size will be probed
+set _FLASHNAME $_CHIPNAME.flash
+flash bank $_FLASHNAME stm32lx 0x08000000 0 0 0 $_TARGETNAME
+
+# if srst is not fitted use SYSRESETREQ to
+# perform a soft reset
+cortex_m3 reset_config sysresetreq
+
+proc stm32l_enable_HSI {} {
+       # Enable HSI as clock source
+       echo "STM32L: Enabling HSI"
+       
+       # Set HSION in RCC_CR
+       mww 0x40023800 0x00000101
+       
+       # Set HSI as SYSCLK
+       mww 0x40023808 0x00000001
+       
+       # Increase JTAG speed
+       adapter_khz 2000
+}
+
+$_TARGETNAME configure -event reset-init {
+       stm32l_enable_HSI
+}
+

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)