From 3ed4207e8ac0b451ebee5b46e9f7ad18f9db42fb Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Sat, 28 Mar 2015 13:04:20 -0700 Subject: stm32l0: Update to use new buffered flash writes. Remove old stubs. --- flashstub/dump-to-array.sh | 11 - flashstub/stm32l05x-nvm-prog-erase.cc | 93 ---- flashstub/stm32l05x-nvm-prog-erase.stub | 67 --- flashstub/stm32l05x-nvm-prog-write.cc | 113 ----- flashstub/stm32l05x-nvm-prog-write.stub | 99 ----- src/stm32l0.c | 723 +++++++++----------------------- 6 files changed, 196 insertions(+), 910 deletions(-) delete mode 100755 flashstub/dump-to-array.sh delete mode 100644 flashstub/stm32l05x-nvm-prog-erase.cc delete mode 100644 flashstub/stm32l05x-nvm-prog-erase.stub delete mode 100644 flashstub/stm32l05x-nvm-prog-write.cc delete mode 100644 flashstub/stm32l05x-nvm-prog-write.stub diff --git a/flashstub/dump-to-array.sh b/flashstub/dump-to-array.sh deleted file mode 100755 index 78584d0..0000000 --- a/flashstub/dump-to-array.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -# -# Convert the output of objdump to an array of half-words that can be -# included into C code to represent the stub. -# -# Invoke with something like this: -# -# objdump -z -d FILE.o | dump-to-array.sh > FILE.stub -# - -sed -E "/^[ ][ ]*[0-9a-fA-F]+:/!d; s/([0-9a-fA-F]+):[ \t]+([0-9a-fA-F]+).*/[0x\1\/2] = 0x\2,/ ; s/0x(....)(....),/0x\2, 0x\1,/" diff --git a/flashstub/stm32l05x-nvm-prog-erase.cc b/flashstub/stm32l05x-nvm-prog-erase.cc deleted file mode 100644 index 58030e3..0000000 --- a/flashstub/stm32l05x-nvm-prog-erase.cc +++ /dev/null @@ -1,93 +0,0 @@ -/* @file stm32l05x-nvm-prog-erase.cc - * - * This file is part of the Black Magic Debug project. - * - * Copyright (C) 2014 Woollysoft - * Written by Marc Singer - * - * 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 3 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 . - */ - -/* ----------- - DESCRIPTION - ----------- - - NVM program flash erase stub for STM32L05x, a Cortex-M0+ core. The - stub uses SRAM to host the code fragment to perform the erase. - - This stub works with the STM32L1xx given a few options. - - If you plan to modify this routine and emit a new stub, make sure - to audit the code. We don't have a stack so we cannot make calls - that save the link pointer. IOW, the inline functions should be be - inlined. - -*/ - -#include -#include -#include "../src/include/stm32lx-nvm.h" - -/* Erase a region of flash. In the event that the erase is misaligned - with respect to pages, it will erase the pages that contain the - requested range of bytes. */ -extern "C" void __attribute((naked)) stm32l05x_nvm_prog_erase() { - // Leave room for INFO at second word of routine - __asm volatile ("b 0f\n\t" - ".align 2\n\t" - ".word 0\n\t" - ".word 0\n\t" - ".word 0\n\t" - ".word 0\n\t" - ".word 0\n\t" - "0:"); - - auto& nvm = Nvm (Info.nvm); - - // Align to the start of the first page so that we make sure to erase - // all of the target pages. - auto remainder = reinterpret_cast (Info.destination) - & (Info.page_size - 1); - Info.size += remainder; - Info.destination -= remainder/sizeof (*Info.destination); - - if (!unlock(nvm)) - goto quit; - - nvm.sr = STM32Lx_NVM_SR_ERR_M; // Clear errors - - // Enable erasing - nvm.pecr = STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_ERASE; - if ((nvm.pecr & (STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_ERASE)) - != (STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_ERASE)) - goto quit; - - while (Info.size > 0) { - *Info.destination = 0; // Initiate erase - - Info.destination += Info.page_size/sizeof (*Info.destination); - Info.size -= Info.page_size; - } - -quit: - lock(nvm); - __asm volatile ("bkpt"); -} - -/* - Local Variables: - compile-command: "/opt/arm/arm-none-eabi-g++ -mcpu=cortex-m0plus -g -c -std=c++11 -mthumb -o stm32l05x-nvm-prog-erase.o -Os -Wa,-ahndl=stm32l05x-nvm-prog-erase.lst stm32l05x-nvm-prog-erase.cc ; /opt/arm/arm-none-eabi-objdump -d -z stm32l05x-nvm-prog-erase.o | ./dump-to-array.sh > stm32l05x-nvm-prog-erase.stub" - End: - -*/ diff --git a/flashstub/stm32l05x-nvm-prog-erase.stub b/flashstub/stm32l05x-nvm-prog-erase.stub deleted file mode 100644 index e0fea34..0000000 --- a/flashstub/stm32l05x-nvm-prog-erase.stub +++ /dev/null @@ -1,67 +0,0 @@ - [0x0/2] = 0xe00a, - [0x2/2] = 0x46c0, - [0x4/2] = 0x0000, 0x0000, - [0x8/2] = 0x0000, 0x0000, - [0xc/2] = 0x0000, 0x0000, - [0x10/2] = 0x0000, 0x0000, - [0x14/2] = 0x0000, 0x0000, - [0x18/2] = 0x491a, - [0x1a/2] = 0x8a08, - [0x1c/2] = 0x680c, - [0x1e/2] = 0x684d, - [0x20/2] = 0x1e42, - [0x22/2] = 0x4022, - [0x24/2] = 0x1955, - [0x26/2] = 0x0892, - [0x28/2] = 0x0092, - [0x2a/2] = 0x1aa2, - [0x2c/2] = 0x600a, - [0x2e/2] = 0x2201, - [0x30/2] = 0x68cb, - [0x32/2] = 0x604d, - [0x34/2] = 0x605a, - [0x36/2] = 0x4a14, - [0x38/2] = 0x60da, - [0x3a/2] = 0x4a14, - [0x3c/2] = 0x60da, - [0x3e/2] = 0x4a14, - [0x40/2] = 0x611a, - [0x42/2] = 0x4a14, - [0x44/2] = 0x611a, - [0x46/2] = 0x685a, - [0x48/2] = 0x0792, - [0x4a/2] = 0xd502, - [0x4c/2] = 0x2201, - [0x4e/2] = 0x605a, - [0x50/2] = 0xbe00, - [0x52/2] = 0x4a11, - [0x54/2] = 0x619a, - [0x56/2] = 0x2282, - [0x58/2] = 0x0092, - [0x5a/2] = 0x605a, - [0x5c/2] = 0x685c, - [0x5e/2] = 0x4014, - [0x60/2] = 0x4294, - [0x62/2] = 0xd1f3, - [0x64/2] = 0x0884, - [0x66/2] = 0x00a4, - [0x68/2] = 0x684d, - [0x6a/2] = 0x4a06, - [0x6c/2] = 0x2d00, - [0x6e/2] = 0xdded, - [0x70/2] = 0x2600, - [0x72/2] = 0x6815, - [0x74/2] = 0x602e, - [0x76/2] = 0x6815, - [0x78/2] = 0x192d, - [0x7a/2] = 0x6015, - [0x7c/2] = 0x6855, - [0x7e/2] = 0x1a2d, - [0x80/2] = 0x6055, - [0x82/2] = 0xe7f1, - [0x84/2] = 0x0004, 0x2000, - [0x88/2] = 0xcdef, 0x89ab, - [0x8c/2] = 0x0405, 0x0203, - [0x90/2] = 0xaebf, 0x8c9d, - [0x94/2] = 0x1516, 0x1314, - [0x98/2] = 0x0700, 0x0001, diff --git a/flashstub/stm32l05x-nvm-prog-write.cc b/flashstub/stm32l05x-nvm-prog-write.cc deleted file mode 100644 index e90401e..0000000 --- a/flashstub/stm32l05x-nvm-prog-write.cc +++ /dev/null @@ -1,113 +0,0 @@ -/* @file stm32l05x-nvm-prog-write.cc - * - * This file is part of the Black Magic Debug project. - * - * Copyright (C) 2014 Woollysoft - * Written by Marc Singer - * - * 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 3 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 . - */ - -/* ----------- - DESCRIPTION - ----------- - - NVM program flash writing stub for STM32L05x, a Cortex-M0+ core. - The stub uses SRAM to host the code fragment and source data to - perform a write to flash. - - This stub works with the STM32L1xx given a few options. - - If you plan to modify this routine and emit a new stub, make sure - to audit the code. We don't have a stack so we cannot make calls - that save the link pointer. IOW, the inline functions should be be - inlined. - -*/ - -#include -#include -#include "../src/include/stm32lx-nvm.h" - -/* Write a block of bytes to flash. The called is responsible for - making sure that the address are aligned and that the count is an - even multiple of words. */ -extern "C" void __attribute((naked)) stm32l05x_nvm_prog_write() { - // Leave room for INFO at second word of routine - __asm volatile ("b 0f\n\t" - ".align 2\n\t" - ".word 0\n\t" - ".word 0\n\t" - ".word 0\n\t" - ".word 0\n\t" - ".word 0\n\t" - "0:"); - - auto& nvm = Nvm (Info.nvm); - - if (!unlock(nvm)) - goto quit; - - nvm.sr = STM32Lx_NVM_SR_ERR_M; // Clear errors - - while (Info.size > 0) { - - // Either we're not half-page aligned or we have less - // than a half page to write - if (Info.size < Info.page_size/2 - || (reinterpret_cast (Info.destination) - & (Info.page_size/2 - 1))) { - nvm.pecr = (Info.options & OPT_STM32L1) ? 0 - : STM32Lx_NVM_PECR_PROG; // Word programming - size_t c = Info.page_size/2 - - (reinterpret_cast (Info.destination) - & (Info.page_size/2 - 1)); - if (c > Info.size) - c = Info.size; - Info.size -= c; - c /= 4; - while (c--) { - uint32_t v = *Info.source++; - *Info.destination++ = v; - if (nvm.sr & STM32Lx_NVM_SR_ERR_M) - goto quit; - } - } - // Or we are writing a half-page(s) - else { - nvm.pecr = STM32Lx_NVM_PECR_PROG - | STM32Lx_NVM_PECR_FPRG; // Half-page prg - size_t c = Info.size & ~(Info.page_size/2 - 1); - Info.size -= c; - c /= 4; - while (c--) { - uint32_t v = *Info.source++; - *Info.destination++ = v; - } - if (nvm.sr & STM32Lx_NVM_SR_ERR_M) - goto quit; - } - } - -quit: - lock(nvm); - __asm volatile ("bkpt"); -} - -/* - Local Variables: - compile-command: "/opt/arm/arm-none-eabi-g++ -mcpu=cortex-m0plus -g -c -std=c++11 -mthumb -o stm32l05x-nvm-prog-write.o -Os -Wa,-ahndl=stm32l05x-nvm-prog-write.lst stm32l05x-nvm-prog-write.cc ; /opt/arm/arm-none-eabi-objdump -d -z stm32l05x-nvm-prog-write.o | ./dump-to-array.sh > stm32l05x-nvm-prog-write.stub" - End: - -*/ diff --git a/flashstub/stm32l05x-nvm-prog-write.stub b/flashstub/stm32l05x-nvm-prog-write.stub deleted file mode 100644 index 7ddbc81..0000000 --- a/flashstub/stm32l05x-nvm-prog-write.stub +++ /dev/null @@ -1,99 +0,0 @@ - [0x0/2] = 0xe00a, - [0x2/2] = 0x46c0, - [0x4/2] = 0x0000, 0x0000, - [0x8/2] = 0x0000, 0x0000, - [0xc/2] = 0x0000, 0x0000, - [0x10/2] = 0x0000, 0x0000, - [0x14/2] = 0x0000, 0x0000, - [0x18/2] = 0x2201, - [0x1a/2] = 0x4b2a, - [0x1c/2] = 0x68d9, - [0x1e/2] = 0x604a, - [0x20/2] = 0x4a29, - [0x22/2] = 0x60ca, - [0x24/2] = 0x4a29, - [0x26/2] = 0x60ca, - [0x28/2] = 0x4a29, - [0x2a/2] = 0x610a, - [0x2c/2] = 0x4a29, - [0x2e/2] = 0x610a, - [0x30/2] = 0x684a, - [0x32/2] = 0x0792, - [0x34/2] = 0xd502, - [0x36/2] = 0x2301, - [0x38/2] = 0x604b, - [0x3a/2] = 0xbe00, - [0x3c/2] = 0x4826, - [0x3e/2] = 0x6188, - [0x40/2] = 0x685d, - [0x42/2] = 0x4e20, - [0x44/2] = 0x2d00, - [0x46/2] = 0xddf6, - [0x48/2] = 0x8a32, - [0x4a/2] = 0x0852, - [0x4c/2] = 0x1e54, - [0x4e/2] = 0x4295, - [0x50/2] = 0xdb02, - [0x52/2] = 0x6837, - [0x54/2] = 0x4227, - [0x56/2] = 0xd01d, - [0x58/2] = 0x2602, - [0x5a/2] = 0x8a5f, - [0x5c/2] = 0x4037, - [0x5e/2] = 0x427e, - [0x60/2] = 0x417e, - [0x62/2] = 0x00f6, - [0x64/2] = 0x604e, - [0x66/2] = 0x681e, - [0x68/2] = 0x4034, - [0x6a/2] = 0x1b12, - [0x6c/2] = 0x42aa, - [0x6e/2] = 0xd900, - [0x70/2] = 0x1c2a, - [0x72/2] = 0x1aad, - [0x74/2] = 0x605d, - [0x76/2] = 0x0892, - [0x78/2] = 0x3a01, - [0x7a/2] = 0xd3e1, - [0x7c/2] = 0x689c, - [0x7e/2] = 0x1d25, - [0x80/2] = 0x609d, - [0x82/2] = 0x6825, - [0x84/2] = 0x681c, - [0x86/2] = 0x1d26, - [0x88/2] = 0x601e, - [0x8a/2] = 0x6025, - [0x8c/2] = 0x698c, - [0x8e/2] = 0x4204, - [0x90/2] = 0xd0f2, - [0x92/2] = 0xe7d0, - [0x94/2] = 0x2481, - [0x96/2] = 0x4252, - [0x98/2] = 0x402a, - [0x9a/2] = 0x1aad, - [0x9c/2] = 0x00e4, - [0x9e/2] = 0x604c, - [0xa0/2] = 0x0892, - [0xa2/2] = 0x6075, - [0xa4/2] = 0x3a01, - [0xa6/2] = 0xd308, - [0xa8/2] = 0x689c, - [0xaa/2] = 0x1d25, - [0xac/2] = 0x609d, - [0xae/2] = 0x6825, - [0xb0/2] = 0x681c, - [0xb2/2] = 0x1d26, - [0xb4/2] = 0x601e, - [0xb6/2] = 0x6025, - [0xb8/2] = 0xe7f4, - [0xba/2] = 0x698a, - [0xbc/2] = 0x4202, - [0xbe/2] = 0xd0bf, - [0xc0/2] = 0xe7b9, - [0xc2/2] = 0x46c0, - [0xc4/2] = 0x0004, 0x2000, - [0xc8/2] = 0xcdef, 0x89ab, - [0xcc/2] = 0x0405, 0x0203, - [0xd0/2] = 0xaebf, 0x8c9d, - [0xd4/2] = 0x1516, 0x1314, - [0xd8/2] = 0x0700, 0x0001, diff --git a/src/stm32l0.c b/src/stm32l0.c index 8193b12..e46ac27 100644 --- a/src/stm32l0.c +++ b/src/stm32l0.c @@ -38,27 +38,6 @@ NOTES ===== - o Stubbed and non-stubbed NVM operation functions. The STM32L0xx - appears to behave differently from other STM32 cores. When it - enters a fault state it will not exit this state without a - reset. However, the reset will immediately enter a fault state - if program flash is erased. When in this state, it will not - permit execution of code in RAM in the way that other cores - will. Changing the PC to the start of RAM and single stepping - will immediately HardFault. - - The stub functions can be both faster and simpler because they - have direct access to the MCU. So, to permit stub operation in - the best circumstances, the NVM operation functions will check - the MCU state and either execute the stub or non-stub version - accordingly. The user can override stubs as well with a command - in case the detection feature fails...which it seems to do in - most cases. - - o Erase would be more efficient if we checked for non-blank-ness - before initiating an erase. This would have to be done in a stub - for efficiency. - o Mass erase unimplemented. The method for performing a mass erase is to set the options for read protection, reload the option bytes, set options for no protection, and then reload the option @@ -88,28 +67,10 @@ o There are minor inconsistencies between the stm32l0 and the stm32l1 in when handling NVM operations. - o When we erase or write individual words (not half-pages) on the - stm32l0, we set the PROG bit. On the stm32l1 the PROG bit is - only set when erasing. This is not documented in the register - summaries, but in the functional quick reference. Argh. - o On the STM32L1xx, PECR can only be changed when the NVM hardware is idle. The STM32L0xx allows the PECR to be updated while an operation is in progress. - o Performance. The throughput for writing is not high. We - suspected it may be possible to improve throughput significantly - by increasing the MCU clock. The code, as is, offers a - simplicity without undue knowledge of the inner workings of the - MCUs. Increasing clock frequencies would require substantial - knowledge of the clock tree. - - FWIW, this was tried. We verified that the system clocks were - changed, but the flash write was no faster. It looks like this - is due to the fact that the emulator performs a target reset - before executing the flash operations, bringing the system back - to the reset state clocking. - */ #include "general.h" @@ -121,31 +82,24 @@ #include "stm32lx-nvm.h" -static int inhibit_stubs; /* Local option to force non-stub flash IO */ - -static int stm32lx_nvm_erase(target *t, uint32_t addr, size_t len); -static int stm32lx_nvm_write(target *t, uint32_t dest, const uint8_t* src, - size_t size); - -static int stm32lx_nvm_prog_erase(target *t, uint32_t addr, size_t len); -static int stm32lx_nvm_prog_write(target *t, uint32_t dest, const uint8_t* src, +static int stm32lx_nvm_prog_erase(struct target_flash* f, + uint32_t addr, size_t len); +static int stm32lx_nvm_prog_write(struct target_flash* f, + uint32_t destination, + const void* src, size_t size); -static int stm32lx_nvm_prog_erase_stubbed(target *t, uint32_t addr, size_t len); -static int stm32lx_nvm_prog_write_stubbed(target *t, uint32_t dest, - const uint8_t* src, size_t size); - -static int stm32lx_nvm_data_erase(target *t, uint32_t addr, size_t len); -static int stm32lx_nvm_data_write(target *t, uint32_t dest, - const uint8_t* src, size_t size); +static int stm32lx_nvm_data_erase(struct target_flash* f, + uint32_t addr, size_t len); +static int stm32lx_nvm_data_write(struct target_flash* f, + uint32_t destination, + const void* source, + size_t size); -static bool stm32lx_cmd_option(target* t, int argc, char** argv); -static bool stm32lx_cmd_eeprom(target* t, int argc, char** argv); -static bool stm32lx_cmd_stubs(target* t, int argc, char** argv); +static bool stm32lx_cmd_option (target* t, int argc, char** argv); +static bool stm32lx_cmd_eeprom (target* t, int argc, char** argv); static const struct command_s stm32lx_cmd_list[] = { - { "stubs", (cmd_handler) stm32lx_cmd_stubs, - "Enable/disable NVM operation stubs" }, { "option", (cmd_handler) stm32lx_cmd_option, "Manipulate option bytes"}, { "eeprom", (cmd_handler) stm32lx_cmd_eeprom, @@ -158,63 +112,7 @@ enum { STM32L1_DBGMCU_IDCODE_PHYS = 0xe0042000, }; -static const char stm32l0_driver_str[] = "STM32L0xx"; - -static const char stm32l0_xml_memory_map[] = "" -/* ""*/ - "" - /* Program flash; ranges up to 64KiB(0x10000). */ - " " - " 0x80" - " " - /* Data(EEPROM) NVRAM; ranges up to 2KiB(0x800). */ - " " - " 0x4" - " " - /* SRAM; ranges up to 8KiB(0x2000). */ - " " - ""; - -static const char stm32l1_driver_str[] = "STM32L1xx"; - -static const char stm32l1_xml_memory_map[] = "" -/* ""*/ - "" - /* Program flash; ranges from 32KiB to 512KiB(0x80000). */ - " " - " 0x100" - " " - /* Data(EEPROM) NVRAM; ranges from 2K to 16KiB(0x4000). */ - " " - " 0x4" - " " - /* SRAM; ranges from 4KiB to 80KiB(0x14000). */ - " " - ""; - -static const uint16_t stm32l0_nvm_prog_write_stub [] = { -#include "../flashstub/stm32l05x-nvm-prog-write.stub" -}; - -static const uint16_t stm32l0_nvm_prog_erase_stub [] = { -#include "../flashstub/stm32l05x-nvm-prog-erase.stub" -}; - -static uint32_t stm32lx_nvm_prog_page_size(target *t) -{ - switch (t->idcode) { - case 0x417: /* STM32L0xx */ - return STM32L0_NVM_PROG_PAGE_SIZE; - default: /* STM32L1xx */ - return STM32L1_NVM_PROG_PAGE_SIZE; - } -} - -static bool stm32lx_is_stm32l1(target *t) +static bool stm32lx_is_stm32l1(target* t) { switch (t->idcode) { case 0x417: /* STM32L0xx */ @@ -244,16 +142,6 @@ static uint32_t stm32lx_nvm_phys(target *t) } } -static uint32_t stm32lx_nvm_data_page_size(target *t) -{ - switch (t->idcode) { - case 0x417: /* STM32L0xx */ - return STM32L0_NVM_DATA_PAGE_SIZE; - default: /* STM32L1xx */ - return STM32L1_NVM_DATA_PAGE_SIZE; - } -} - static uint32_t stm32lx_nvm_option_size(target *t) { switch (t->idcode) { @@ -264,45 +152,69 @@ static uint32_t stm32lx_nvm_option_size(target *t) } } +static void stm32l_add_flash(target *t, + uint32_t addr, size_t length, size_t erasesize) +{ + struct target_flash *f = calloc(1, sizeof(*f)); + f->start = addr; + f->length = length; + f->blocksize = erasesize; + f->erase = stm32lx_nvm_prog_erase; + f->write = target_flash_write_buffered; + f->done = target_flash_done_buffered; + f->write_buf = stm32lx_nvm_prog_write; + f->buf_size = erasesize/2; + target_add_flash(t, f); +} + +static void stm32l_add_eeprom(target *t, uint32_t addr, size_t length) +{ + struct target_flash *f = calloc(1, sizeof(*f)); + f->start = addr; + f->length = length; + f->blocksize = 4; + f->erase = stm32lx_nvm_data_erase; + f->write = stm32lx_nvm_data_write; + f->align = 1; + target_add_flash(t, f); +} + /** Query MCU memory for an indication as to whether or not the currently attached target is served by this module. We detect the STM32L0xx parts as well as the STM32L1xx's. */ -bool stm32l0_probe(target *t) +bool stm32l0_probe(target* t) { - uint32_t idcode; - - idcode = target_mem_read32(t, STM32L1_DBGMCU_IDCODE_PHYS) & 0xfff; - switch (idcode) { - case 0x416: /* CAT. 1 device */ - case 0x429: /* CAT. 2 device */ - case 0x427: /* CAT. 3 device */ - case 0x436: /* CAT. 4 device */ - case 0x437: /* CAT. 5 device */ - t->idcode = idcode; - t->driver = stm32l1_driver_str; - t->xml_mem_map = stm32l1_xml_memory_map; - t->flash_erase = stm32lx_nvm_erase; - t->flash_write = stm32lx_nvm_write; - target_add_commands(t, stm32lx_cmd_list, "STM32L1x"); - return true; - } - - idcode = target_mem_read32(t, STM32L0_DBGMCU_IDCODE_PHYS) & 0xfff; - switch (idcode) { - default: - break; - - case 0x417: /* STM32L0x[123] & probably others */ - t->idcode = idcode; - t->driver = stm32l0_driver_str; - t->xml_mem_map = stm32l0_xml_memory_map; - t->flash_erase = stm32lx_nvm_erase; - t->flash_write = stm32lx_nvm_write; - target_add_commands(t, stm32lx_cmd_list, "STM32L0x"); - return true; - } - - return false; + uint32_t idcode; + + idcode = target_mem_read32(t, STM32L1_DBGMCU_IDCODE_PHYS) & 0xfff; + switch (idcode) { + case 0x416: /* CAT. 1 device */ + case 0x429: /* CAT. 2 device */ + case 0x427: /* CAT. 3 device */ + case 0x436: /* CAT. 4 device */ + case 0x437: /* CAT. 5 device */ + t->idcode = idcode; + t->driver = "STM32L1x"; + target_add_ram(t, 0x20000000, 0x14000); + stm32l_add_flash(t, 0x8000000, 0x80000, 0x100); + //stm32l_add_eeprom(t, 0x8080000, 0x4000); + target_add_commands(t, stm32lx_cmd_list, "STM32L1x"); + return true; + } + + idcode = target_mem_read32(t, STM32L0_DBGMCU_IDCODE_PHYS) & 0xfff; + switch (idcode) { + case 0x417: /* STM32L0x[123] & probably others */ + t->idcode = idcode; + t->driver = "STM32L0x"; + target_add_ram(t, 0x20000000, 0x2000); + stm32l_add_flash(t, 0x8000000, 0x10000, 0x80); + stm32l_add_eeprom(t, 0x8080000, 0x800); + target_add_commands(t, stm32lx_cmd_list, "STM32L0x"); + return true; + } + + return false; } @@ -346,318 +258,96 @@ static bool stm32lx_nvm_opt_unlock(target *t, uint32_t nvm) & STM32Lx_NVM_PECR_OPTLOCK); } - -/** Erase a region of flash using a stub function. This only works - when the MCU hasn't entered a fault state(see NOTES). The flash - array is erased for all pages from addr to addr+len inclusive. */ -static int stm32lx_nvm_prog_erase_stubbed(target *t, - uint32_t addr, size_t size) -{ - struct stm32lx_nvm_stub_info info; - const uint32_t nvm = stm32lx_nvm_phys(t); - - info.nvm = nvm; - info.page_size = stm32lx_nvm_prog_page_size(t); - - /* Load the stub */ - target_mem_write(t, STM32Lx_STUB_PHYS, - &stm32l0_nvm_prog_erase_stub[0], - sizeof(stm32l0_nvm_prog_erase_stub)); - - /* Setup parameters */ - info.destination = addr; - info.size = size; - - /* Copy parameters */ - target_mem_write(t, STM32Lx_STUB_INFO_PHYS, &info, sizeof(info)); - - /* Execute stub */ - cortexm_run_stub(t, STM32Lx_STUB_PHYS, 0, 0, 0, 0); - - if (target_mem_read32(t, STM32Lx_NVM_SR(nvm)) - & STM32Lx_NVM_SR_ERR_M) - return -1; - - - return 0; -} - - -/** Write to program flash using a stub function. This only works - when the MCU hasn't entered a fault state. Once the MCU faults, - this function will not succeed because the MCU will fault before - executing a single instruction in the stub. */ -static int stm32lx_nvm_prog_write_stubbed(target *t, - uint32_t destination, - const uint8_t* source, - size_t size) -{ - struct stm32lx_nvm_stub_info info; - const uint32_t nvm = stm32lx_nvm_phys(t); - const size_t page_size = stm32lx_nvm_prog_page_size(t); - - /* We can only handle word aligned writes and even - word-multiple ranges. The stm32lx's cannot perform - anything smaller than a word write due to the ECC bits. - So, the caller must do the fixup. */ - if ((destination & 3) || (size & 3)) - return -1; - - info.nvm = nvm; - info.page_size = page_size; - - /* Load the stub */ - target_mem_write(t, STM32Lx_STUB_PHYS, - &stm32l0_nvm_prog_write_stub[0], - sizeof(stm32l0_nvm_prog_write_stub)); - - while (size > 0) { - - /* Max transfer size is adjusted in the event that the - destination isn't half-page aligned. This allows - the stub to write the first partial half-page and - then as many half-pages as will fit in the - buffer. */ - size_t max = STM32Lx_STUB_DATA_MAX - - (destination - (destination - & ~(info.page_size/2 - 1))); - size_t cb = size; - if (cb > max) - cb = max; - - /* Setup parameters */ - info.source = STM32Lx_STUB_DATA_PHYS; - info.destination = destination; - info.size = cb; - - /* Copy data to write to flash */ - target_mem_write(t, info.source, source, info.size); - - /* Move pointers early */ - destination += cb; - source += cb; - size -= cb; - - /* Copy parameters */ - target_mem_write(t, STM32Lx_STUB_INFO_PHYS, - &info, sizeof(info)); - - /* Execute stub */ - cortexm_run_stub(t, STM32Lx_STUB_PHYS, 0, 0, 0, 0); - - if (target_mem_read32(t, STM32Lx_NVM_SR(nvm)) - & STM32Lx_NVM_SR_ERR_M) - return -1; - } - - return 0; -} - - -/** Erase a region of NVM for STM32Lx. This is the lead function and - it will invoke an implementation, stubbed or not depending on the - options and the range of addresses. */ -static int stm32lx_nvm_erase(target *t, uint32_t addr, size_t size) -{ - if (addr >= STM32Lx_NVM_EEPROM_PHYS) - return stm32lx_nvm_data_erase(t, addr, size); - - /* Use stub if not inhibited, the MCU is in a non-exceptonal state - and there is stub. */ - volatile uint32_t regs[20]; - target_regs_read(t, ®s); - if (inhibit_stubs || (regs[16] & 0xf)) - return stm32lx_nvm_prog_erase(t, addr, size); - - return stm32lx_nvm_prog_erase_stubbed(t, addr, size); -} - - -/** Write to a region on NVM for STM32Lxxx. This is the lead function - and it will invoke an implementation, stubbed or not depending on - the options and the range of addresses. Data (EEPROM) writes - don't have to care about alignment, but the program flash does. - There is a fixup for unaligned program flash writes. */ -static int stm32lx_nvm_write(target *t, - uint32_t destination, - const uint8_t* source, - size_t size) -{ - if (destination >= STM32Lx_NVM_EEPROM_PHYS) - return stm32lx_nvm_data_write(t, destination, source, - size); - - /* Unaligned destinations. To make this feature simple to - implement, we do a fixup on the source data as well as the - adjusting the write parameters if the caller has asked for - an unaligned operation. Padding of this data is zeros - because the STM32L's are built that way. */ - if ((destination & 3) || (size & 3)) { - size_t size_aligned = size - + (destination & 3) - + (((size + 3) & ~3) - size); - uint8_t* source_aligned = alloca (size_aligned); - memset (source_aligned, 0, size_aligned); - memcpy (source_aligned + (destination & 3), source, size); - source = source_aligned; - destination &= ~3; - size = size_aligned; - } - - /* Skip stub if the MCU is in a questionable state, or if the - user asks us to avoid stubs. */ - volatile uint32_t regs[20]; - target_regs_read(t, ®s); - if (inhibit_stubs || (regs[16] & 0xf)) - return stm32lx_nvm_prog_write(t, destination, source, - size); - - return stm32lx_nvm_prog_write_stubbed(t, destination, source, - size); -} - - /** Erase a region of program flash using operations through the debug interface. This is slower than stubbed versions(see NOTES). The flash array is erased for all pages from addr to addr+len inclusive. NVM register file address chosen from target. */ -static int stm32lx_nvm_prog_erase(target *t, uint32_t addr, size_t len) +static int stm32lx_nvm_prog_erase(struct target_flash* f, + uint32_t addr, size_t len) { - const size_t page_size = stm32lx_nvm_prog_page_size(t); - const uint32_t nvm = stm32lx_nvm_phys(t); - - /* Word align */ - len += (addr & 3); - addr &= ~3; + target *t = f->t; + const size_t page_size = f->blocksize; + const uint32_t nvm = stm32lx_nvm_phys(t); - if (!stm32lx_nvm_prog_data_unlock(t, nvm)) - return -1; + if (!stm32lx_nvm_prog_data_unlock(t, nvm)) + return -1; - /* Flash page erase instruction */ - target_mem_write32(t, STM32Lx_NVM_PECR(nvm), - STM32Lx_NVM_PECR_ERASE | STM32Lx_NVM_PECR_PROG); + /* Flash page erase instruction */ + target_mem_write32(t, STM32Lx_NVM_PECR(nvm), + STM32Lx_NVM_PECR_ERASE | STM32Lx_NVM_PECR_PROG); uint32_t pecr = target_mem_read32(t, STM32Lx_NVM_PECR(nvm)); if ((pecr & (STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_ERASE)) != (STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_ERASE)) return -1; - /* Clear errors. Note that this only works when we wait for the NVM - block to complete the last operation. */ - target_mem_write32(t, STM32Lx_NVM_SR(nvm), STM32Lx_NVM_SR_ERR_M); + /* Clear errors. Note that this only works when we wait for the NVM + block to complete the last operation. */ + target_mem_write32(t, STM32Lx_NVM_SR(nvm), STM32Lx_NVM_SR_ERR_M); - while (len > 0) { - /* Write first word of page to 0 */ - target_mem_write32(t, addr, 0); + while (len > 0) { + /* Write first word of page to 0 */ + target_mem_write32(t, addr, 0); - len -= page_size; - addr += page_size; - } + len -= page_size; + addr += page_size; + } - /* Disable further programming by locking PECR */ - stm32lx_nvm_lock(t, nvm); + /* Disable further programming by locking PECR */ + stm32lx_nvm_lock(t, nvm); - /* Wait for completion or an error */ - while (1) { - uint32_t sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm)); - if (target_check_error(t)) - return -1; - if (sr & STM32Lx_NVM_SR_BSY) - continue; - if ((sr & STM32Lx_NVM_SR_ERR_M) || !(sr & STM32Lx_NVM_SR_EOP)) - return -1; - break; - } + /* Wait for completion or an error */ + uint32_t sr; + do { + sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm)); + } while (sr & STM32Lx_NVM_SR_BSY); + + if ((sr & STM32Lx_NVM_SR_ERR_M) || !(sr & STM32Lx_NVM_SR_EOP) || + target_check_error(t)) + return -1; - return 0; + return 0; } /** Write to program flash using operations through the debug - interface. This is slower than the stubbed write(see NOTES). - NVM register file address chosen from target. */ -static int stm32lx_nvm_prog_write(target *t, - uint32_t destination, - const uint8_t* source_8, + interface. */ +static int stm32lx_nvm_prog_write(struct target_flash *f, + uint32_t dest, + const void* src, size_t size) { - const uint32_t nvm = stm32lx_nvm_phys(t); - const bool is_stm32l1 = stm32lx_is_stm32l1(t); - - /* We can only handle word aligned writes and even - word-multiple ranges. The stm32lx's cannot perform - anything smaller than a word write due to the ECC bits. - So, the caller must do the fixup. */ - if ((destination & 3) || (size & 3)) - return -1; - - if (!stm32lx_nvm_prog_data_unlock(t, nvm)) - return -1; - - const size_t half_page_size = stm32lx_nvm_prog_page_size(t)/2; - uint32_t* source = (uint32_t*) source_8; - - while (size > 0) { - - /* Wait for BSY to clear because we cannot write the PECR until - the previous operation completes on STM32Lxxx. */ - while (target_mem_read32(t, STM32Lx_NVM_SR(nvm)) - & STM32Lx_NVM_SR_BSY) - if (target_check_error(t)) { - return -1; - } - - // Either we're not half-page aligned or we have less - // than a half page to write - if (size < half_page_size - || (destination & (half_page_size - 1))) { - target_mem_write32(t, STM32Lx_NVM_PECR(nvm), - is_stm32l1 - ? 0 - : STM32Lx_NVM_PECR_PROG); - size_t c = half_page_size - (destination - & (half_page_size - 1)); - - if (c > size) - c = size; - size -= c; - - target_mem_write(t, destination, source, c); - source += c/4; - destination += c; - } - // Or we are writing a half-page(s) - else { - target_mem_write32(t, STM32Lx_NVM_PECR(nvm), - STM32Lx_NVM_PECR_PROG - | STM32Lx_NVM_PECR_FPRG); - - size_t c = size & ~(half_page_size - 1); - size -= c; - target_mem_write(t, destination, source, c); - source += c/4; - destination += c; - } - } + target *t = f->t; + const uint32_t nvm = stm32lx_nvm_phys(t); - /* Disable further programming by locking PECR */ - stm32lx_nvm_lock(t, nvm); + if (!stm32lx_nvm_prog_data_unlock(t, nvm)) + return -1; - /* Wait for completion or an error */ - while (1) { - uint32_t sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm)); - if (target_check_error(t)) { - return -1; - } - if (sr & STM32Lx_NVM_SR_BSY) - continue; - if ((sr & STM32Lx_NVM_SR_ERR_M) || !(sr & STM32Lx_NVM_SR_EOP)) { - return -1; - } - break; - } + /* Wait for BSY to clear because we cannot write the PECR until + the previous operation completes on STM32Lxxx. */ + while (target_mem_read32(t, STM32Lx_NVM_SR(nvm)) + & STM32Lx_NVM_SR_BSY) + if (target_check_error(t)) + return -1; + + target_mem_write32(t, STM32Lx_NVM_PECR(nvm), + STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_FPRG); + target_mem_write(t, dest, src, size); + + /* Disable further programming by locking PECR */ + stm32lx_nvm_lock(t, nvm); + + /* Wait for completion or an error */ + uint32_t sr; + do { + sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm)); + } while (sr & STM32Lx_NVM_SR_BSY); + + if ((sr & STM32Lx_NVM_SR_ERR_M) || !(sr & STM32Lx_NVM_SR_EOP) || + target_check_error(t)) + return -1; - return 0; + return 0; } @@ -665,52 +355,51 @@ static int stm32lx_nvm_prog_write(target *t, interface . The flash is erased for all pages from addr to addr+len, inclusive, on a word boundary. NVM register file address chosen from target. */ -static int stm32lx_nvm_data_erase(target *t, +static int stm32lx_nvm_data_erase(struct target_flash *f, uint32_t addr, size_t len) { - const size_t page_size = stm32lx_nvm_data_page_size(t); - const uint32_t nvm = stm32lx_nvm_phys(t); + target *t = f->t; + const size_t page_size = f->blocksize; + const uint32_t nvm = stm32lx_nvm_phys(t); - /* Word align */ - len += (addr & 3); - addr &= ~3; + /* Word align */ + len += (addr & 3); + addr &= ~3; - if (!stm32lx_nvm_prog_data_unlock(t, nvm)) - return -1; + if (!stm32lx_nvm_prog_data_unlock(t, nvm)) + return -1; - /* Flash data erase instruction */ - target_mem_write32(t, STM32Lx_NVM_PECR(nvm), - STM32Lx_NVM_PECR_ERASE | STM32Lx_NVM_PECR_DATA); + /* Flash data erase instruction */ + target_mem_write32(t, STM32Lx_NVM_PECR(nvm), + STM32Lx_NVM_PECR_ERASE | STM32Lx_NVM_PECR_DATA); uint32_t pecr = target_mem_read32(t, STM32Lx_NVM_PECR(nvm)); if ((pecr & (STM32Lx_NVM_PECR_ERASE | STM32Lx_NVM_PECR_DATA)) != (STM32Lx_NVM_PECR_ERASE | STM32Lx_NVM_PECR_DATA)) return -1; - while (len > 0) { - /* Write first word of page to 0 */ - target_mem_write32(t, addr, 0); + while (len > 0) { + /* Write first word of page to 0 */ + target_mem_write32(t, addr, 0); - len -= page_size; - addr += page_size; - } + len -= page_size; + addr += page_size; + } - /* Disable further programming by locking PECR */ - stm32lx_nvm_lock(t, nvm); + /* Disable further programming by locking PECR */ + stm32lx_nvm_lock(t, nvm); - /* Wait for completion or an error */ - while (1) { - uint32_t sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm)); - if (target_check_error(t)) - return -1; - if (sr & STM32Lx_NVM_SR_BSY) - continue; - if ((sr & STM32Lx_NVM_SR_ERR_M) || !(sr & STM32Lx_NVM_SR_EOP)) - return -1; - break; - } + /* Wait for completion or an error */ + uint32_t sr; + do { + sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm)); + } while (sr & STM32Lx_NVM_SR_BSY); + + if ((sr & STM32Lx_NVM_SR_ERR_M) || !(sr & STM32Lx_NVM_SR_EOP) || + target_check_error(t)) + return -1; - return 0; + return 0; } @@ -718,47 +407,46 @@ static int stm32lx_nvm_data_erase(target *t, NVM register file address chosen from target. Unaligned destination writes are supported (though unaligned sources are not). */ -static int stm32lx_nvm_data_write(target *t, +static int stm32lx_nvm_data_write(struct target_flash *f, uint32_t destination, - const uint8_t* source_8, + const void* src, size_t size) { - const uint32_t nvm = stm32lx_nvm_phys(t); - const bool is_stm32l1 = stm32lx_is_stm32l1(t); - uint32_t* source = (uint32_t*) source_8; + target *t = f->t; + const uint32_t nvm = stm32lx_nvm_phys(t); + const bool is_stm32l1 = stm32lx_is_stm32l1(t); + uint32_t* source = (uint32_t*) src; - if (!stm32lx_nvm_prog_data_unlock(t, nvm)) - return -1; + if (!stm32lx_nvm_prog_data_unlock(t, nvm)) + return -1; - target_mem_write32(t, STM32Lx_NVM_PECR(nvm), - is_stm32l1 ? 0 : STM32Lx_NVM_PECR_DATA); + target_mem_write32(t, STM32Lx_NVM_PECR(nvm), + is_stm32l1 ? 0 : STM32Lx_NVM_PECR_DATA); - while (size) { - size -= 4; - uint32_t v = *source++; - target_mem_write32(t, destination, v); - destination += 4; + while (size) { + size -= 4; + uint32_t v = *source++; + target_mem_write32(t, destination, v); + destination += 4; - if (target_check_error(t)) - return -1; - } + if (target_check_error(t)) + return -1; + } - /* Disable further programming by locking PECR */ - stm32lx_nvm_lock(t, nvm); + /* Disable further programming by locking PECR */ + stm32lx_nvm_lock(t, nvm); - /* Wait for completion or an error */ - while (1) { - uint32_t sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm)); - if (target_check_error(t)) - return -1; - if (sr & STM32Lx_NVM_SR_BSY) - continue; - if ((sr & STM32Lx_NVM_SR_ERR_M) || !(sr & STM32Lx_NVM_SR_EOP)) - return -1; - break; - } + /* Wait for completion or an error */ + uint32_t sr; + do { + sr = target_mem_read32(t, STM32Lx_NVM_SR(nvm)); + } while (sr & STM32Lx_NVM_SR_BSY); + + if ((sr & STM32Lx_NVM_SR_ERR_M) || !(sr & STM32Lx_NVM_SR_EOP) || + target_check_error(t)) + return -1; - return 0; + return 0; } @@ -822,25 +510,6 @@ static bool stm32lx_eeprom_write(target *t, uint32_t address, return !(sr & STM32Lx_NVM_SR_ERR_M); } -static bool stm32lx_cmd_stubs(target* t, - int argc, char** argv) -{ - (void) t; - if (argc == 1) { - gdb_out("usage: mon stubs [enable/disable]\n"); - } - else if (argc == 2) { - size_t cb = strlen(argv[1]); - if (!strncasecmp(argv[1], "enable", cb)) - inhibit_stubs = 0; - if (!strncasecmp(argv[1], "disable", cb)) - inhibit_stubs = 1; - } - gdb_outf("stubs: %sabled\n", inhibit_stubs ? "dis" : "en"); - - return true; -} - static bool stm32lx_cmd_option(target* t, int argc, char** argv) { const uint32_t nvm = stm32lx_nvm_phys(t); -- cgit v1.2.3