Browse Source

stm32l0: Update to use new buffered flash writes. Remove old stubs.

pull/87/head
Gareth McMullin 10 years ago
parent
commit
3ed4207e8a
  1. 11
      flashstub/dump-to-array.sh
  2. 93
      flashstub/stm32l05x-nvm-prog-erase.cc
  3. 67
      flashstub/stm32l05x-nvm-prog-erase.stub
  4. 113
      flashstub/stm32l05x-nvm-prog-write.cc
  5. 99
      flashstub/stm32l05x-nvm-prog-write.stub
  6. 723
      src/stm32l0.c

11
flashstub/dump-to-array.sh

@ -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,/"

93
flashstub/stm32l05x-nvm-prog-erase.cc

@ -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 <elf@woollysoft.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 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 <http://www.gnu.org/licenses/>.
*/
/* -----------
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 <stdint.h>
#include <string.h>
#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<uint32_t> (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:
*/

67
flashstub/stm32l05x-nvm-prog-erase.stub

@ -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,

113
flashstub/stm32l05x-nvm-prog-write.cc

@ -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 <elf@woollysoft.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 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 <http://www.gnu.org/licenses/>.
*/
/* -----------
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 <stdint.h>
#include <string.h>
#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<uint32_t> (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<uint32_t> (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:
*/

99
flashstub/stm32l05x-nvm-prog-write.stub

@ -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,

723
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[] = "<?xml version=\"1.0\"?>"
/* "<!DOCTYPE memory-map "
" PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\""
" \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"*/
"<memory-map>"
/* Program flash; ranges up to 64KiB(0x10000). */
" <memory type=\"flash\" start=\"0x08000000\" length=\"0x10000\">"
" <property name=\"blocksize\">0x80</property>"
" </memory>"
/* Data(EEPROM) NVRAM; ranges up to 2KiB(0x800). */
" <memory type=\"flash\" start=\"0x08080000\" length=\"0x800\">"
" <property name=\"blocksize\">0x4</property>"
" </memory>"
/* SRAM; ranges up to 8KiB(0x2000). */
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x2000\"/>"
"</memory-map>";
static const char stm32l1_driver_str[] = "STM32L1xx";
static const char stm32l1_xml_memory_map[] = "<?xml version=\"1.0\"?>"
/* "<!DOCTYPE memory-map "
" PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\""
" \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"*/
"<memory-map>"
/* Program flash; ranges from 32KiB to 512KiB(0x80000). */
" <memory type=\"flash\" start=\"0x08000000\" length=\"0x80000\">"
" <property name=\"blocksize\">0x100</property>"
" </memory>"
/* Data(EEPROM) NVRAM; ranges from 2K to 16KiB(0x4000). */
" <memory type=\"flash\" start=\"0x08080000\" length=\"0x4000\">"
" <property name=\"blocksize\">0x4</property>"
" </memory>"
/* SRAM; ranges from 4KiB to 80KiB(0x14000). */
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x14000\"/>"
"</memory-map>";
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, &regs);
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, &regs);
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);

Loading…
Cancel
Save