STM32L0x target support.

Target support for stm32l0's and stm32l1's including option bytes and
data EEPROM.  This module will superceed the previous stm32l1 driver.

o Program flash write and erase.
o Options modification and interpretive status.
o Stubs for program flash writes and erases.  Stubs are modestly
  faster than non-stub version.   The stm32l0 will not execute stubs
  when the MCU has crashed.  A monitor option may be used to force
  non-stub flash writes.
o Stubs generated from C++ code and converted to arrays of half-words.
o Writes to data EEPROM supoprted when loading segments.
o EEPROM data monitor command to write words.
o Stubs supported on stm32l1.
This commit is contained in:
Marc Singer 2014-12-19 16:25:22 -08:00
parent b6d73442cc
commit e0a8ce5a88
11 changed files with 1881 additions and 4 deletions

View File

@ -1,5 +1,13 @@
These are the assembler routines for executing a flash write
on the supported targets. They are kept here for reference, but
are not used, as the compiled binary code is included in the
target drivers.
Flash Stubs
===========
For most of the targets, these are assembler routines for executing
a flash write on the supported targets. They are kept here for
reference, but are not used, as the compiled binary code is included
in the target drivers.
For the STM32l0x, the stubs are written in C++ and emitted as arrays
of half-words for inclusion in the target driver. The use of a higher
level language allows more detailed code and for easy revisions.
These stubs communicate with the driver through a structure defined in
the src/include/stm32l0-nvm.h header.

24
flashstub/code-to-array.pl Executable file
View File

@ -0,0 +1,24 @@
#!/usr/bin/perl
#
# Convert the output of objdump to an array of bytes are can include
# into our program.
while (<>) {
if (m/^\s*([0-9a-fA-F]+):\s*([0-9a-fA-F]+)(.*)/) {
my $addr = "0x$1";
my $value = $2;
if (length ($value) == 4) {
print " [$addr/2] = 0x$value, // $_";
}
else {
my $lsb = substr ($value, 4, 4);
my $msb = substr ($value, 0, 4);
print " [$addr/2] = 0x$lsb, // $_";
print " [$addr/2 + 1] = 0x$msb,\n";
}
}
else {
print "// ", $_;
}
}

View File

@ -0,0 +1,93 @@
/* @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 -S stm32l05x-nvm-prog-erase.o | ./code-to-array.pl > stm32l05x-nvm-prog-erase.stub"
End:
*/

View File

@ -0,0 +1,159 @@
//
// stm32l05x-nvm-prog-erase.o: file format elf32-littlearm
//
//
// Disassembly of section .text:
//
// 00000000 <stm32l05x_nvm_prog_erase>:
// ".word 0\n\t"
// ".word 0\n\t"
// ".word 0\n\t"
// ".word 0\n\t"
// ".word 0\n\t"
// "0:");
[0x0/2] = 0xe00a, // 0: e00a b.n 18 <stm32l05x_nvm_prog_erase+0x18>
[0x2/2] = 0x46c0, // 2: 46c0 nop ; (mov r8, r8)
// ...
//
// auto& nvm = Nvm (Info.nvm);
[0x18/2] = 0x491a, // 18: 491a ldr r1, [pc, #104] ; (84 <stm32l05x_nvm_prog_erase+0x84>)
//
// // 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);
[0x1a/2] = 0x8a08, // 1a: 8a08 ldrh r0, [r1, #16]
[0x1c/2] = 0x680c, // 1c: 680c ldr r4, [r1, #0]
// Info.size += remainder;
[0x1e/2] = 0x684d, // 1e: 684d ldr r5, [r1, #4]
// 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);
[0x20/2] = 0x1e42, // 20: 1e42 subs r2, r0, #1
[0x22/2] = 0x4022, // 22: 4022 ands r2, r4
// Info.size += remainder;
[0x24/2] = 0x1955, // 24: 1955 adds r5, r2, r5
// Info.destination -= remainder/sizeof (*Info.destination);
[0x26/2] = 0x0892, // 26: 0892 lsrs r2, r2, #2
[0x28/2] = 0x0092, // 28: 0092 lsls r2, r2, #2
[0x2a/2] = 0x1aa2, // 2a: 1aa2 subs r2, r4, r2
[0x2c/2] = 0x600a, // 2c: 600a str r2, [r1, #0]
// #define Nvm(nvm) (*reinterpret_cast<STM32::NVM*>(nvm))
// #define Info (*reinterpret_cast<stm32lx_nvm_stub_info*>(STM32Lx_STUB_INFO_PHYS))
//
// namespace {
// inline __attribute((always_inline)) bool unlock (STM32::NVM& nvm) {
// nvm.pecr = STM32Lx_NVM_PECR_PELOCK; // Lock to guarantee unlock
[0x2e/2] = 0x2201, // 2e: 2201 movs r2, #1
// ".word 0\n\t"
// ".word 0\n\t"
// ".word 0\n\t"
// "0:");
//
// auto& nvm = Nvm (Info.nvm);
[0x30/2] = 0x68cb, // 30: 68cb ldr r3, [r1, #12]
//
// // 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;
[0x32/2] = 0x604d, // 32: 604d str r5, [r1, #4]
[0x34/2] = 0x605a, // 34: 605a str r2, [r3, #4]
// nvm.pkeyr = STM32::NVM::PKEY1;
[0x36/2] = 0x4a14, // 36: 4a14 ldr r2, [pc, #80] ; (88 <stm32l05x_nvm_prog_erase+0x88>)
[0x38/2] = 0x60da, // 38: 60da str r2, [r3, #12]
// nvm.pkeyr = STM32::NVM::PKEY2;
[0x3a/2] = 0x4a14, // 3a: 4a14 ldr r2, [pc, #80] ; (8c <stm32l05x_nvm_prog_erase+0x8c>)
[0x3c/2] = 0x60da, // 3c: 60da str r2, [r3, #12]
// nvm.prgkeyr = STM32::NVM::PRGKEY1;
[0x3e/2] = 0x4a14, // 3e: 4a14 ldr r2, [pc, #80] ; (90 <stm32l05x_nvm_prog_erase+0x90>)
[0x40/2] = 0x611a, // 40: 611a str r2, [r3, #16]
// nvm.prgkeyr = STM32::NVM::PRGKEY2;
[0x42/2] = 0x4a14, // 42: 4a14 ldr r2, [pc, #80] ; (94 <stm32l05x_nvm_prog_erase+0x94>)
[0x44/2] = 0x611a, // 44: 611a str r2, [r3, #16]
// return !(nvm.pecr & STM32Lx_NVM_PECR_PRGLOCK);
[0x46/2] = 0x685a, // 46: 685a ldr r2, [r3, #4]
// Info.destination -= remainder/sizeof (*Info.destination);
//
// if (!unlock (nvm))
[0x48/2] = 0x0792, // 48: 0792 lsls r2, r2, #30
[0x4a/2] = 0xd502, // 4a: d502 bpl.n 52 <stm32l05x_nvm_prog_erase+0x52>
// }
// inline __attribute((always_inline)) void lock (STM32::NVM& nvm) {
// nvm.pecr = STM32Lx_NVM_PECR_PELOCK; }
[0x4c/2] = 0x2201, // 4c: 2201 movs r2, #1
[0x4e/2] = 0x605a, // 4e: 605a str r2, [r3, #4]
// Info.size -= Info.page_size;
// }
//
// quit:
// lock (nvm);
// __asm volatile ("bkpt");
[0x50/2] = 0xbe00, // 50: be00 bkpt 0x0000
// Info.destination -= remainder/sizeof (*Info.destination);
//
// if (!unlock (nvm))
// goto quit;
//
// nvm.sr = STM32Lx_NVM_SR_ERR_M; // Clear errors
[0x52/2] = 0x4a11, // 52: 4a11 ldr r2, [pc, #68] ; (98 <stm32l05x_nvm_prog_erase+0x98>)
[0x54/2] = 0x619a, // 54: 619a str r2, [r3, #24]
//
// // Enable erasing
// nvm.pecr = STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_ERASE;
[0x56/2] = 0x2282, // 56: 2282 movs r2, #130 ; 0x82
[0x58/2] = 0x0092, // 58: 0092 lsls r2, r2, #2
[0x5a/2] = 0x605a, // 5a: 605a str r2, [r3, #4]
// if ((nvm.pecr & (STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_ERASE))
[0x5c/2] = 0x685c, // 5c: 685c ldr r4, [r3, #4]
[0x5e/2] = 0x4014, // 5e: 4014 ands r4, r2
[0x60/2] = 0x4294, // 60: 4294 cmp r4, r2
[0x62/2] = 0xd1f3, // 62: d1f3 bne.n 4c <stm32l05x_nvm_prog_erase+0x4c>
// goto quit;
//
// while (Info.size > 0) {
// *Info.destination = 0; // Initiate erase
//
// Info.destination += Info.page_size/sizeof (*Info.destination);
[0x64/2] = 0x0884, // 64: 0884 lsrs r4, r0, #2
[0x66/2] = 0x00a4, // 66: 00a4 lsls r4, r4, #2
// 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) {
[0x68/2] = 0x684d, // 68: 684d ldr r5, [r1, #4]
[0x6a/2] = 0x4a06, // 6a: 4a06 ldr r2, [pc, #24] ; (84 <stm32l05x_nvm_prog_erase+0x84>)
[0x6c/2] = 0x2d00, // 6c: 2d00 cmp r5, #0
[0x6e/2] = 0xdded, // 6e: dded ble.n 4c <stm32l05x_nvm_prog_erase+0x4c>
// *Info.destination = 0; // Initiate erase
[0x70/2] = 0x2600, // 70: 2600 movs r6, #0
[0x72/2] = 0x6815, // 72: 6815 ldr r5, [r2, #0]
[0x74/2] = 0x602e, // 74: 602e str r6, [r5, #0]
//
// Info.destination += Info.page_size/sizeof (*Info.destination);
[0x76/2] = 0x6815, // 76: 6815 ldr r5, [r2, #0]
[0x78/2] = 0x192d, // 78: 192d adds r5, r5, r4
[0x7a/2] = 0x6015, // 7a: 6015 str r5, [r2, #0]
// Info.size -= Info.page_size;
[0x7c/2] = 0x6855, // 7c: 6855 ldr r5, [r2, #4]
[0x7e/2] = 0x1a2d, // 7e: 1a2d subs r5, r5, r0
[0x80/2] = 0x6055, // 80: 6055 str r5, [r2, #4]
[0x82/2] = 0xe7f1, // 82: e7f1 b.n 68 <stm32l05x_nvm_prog_erase+0x68>
[0x84/2] = 0x0004, // 84: 20000004 .word 0x20000004
[0x84/2 + 1] = 0x2000,
[0x88/2] = 0xcdef, // 88: 89abcdef .word 0x89abcdef
[0x88/2 + 1] = 0x89ab,
[0x8c/2] = 0x0405, // 8c: 02030405 .word 0x02030405
[0x8c/2 + 1] = 0x0203,
[0x90/2] = 0xaebf, // 90: 8c9daebf .word 0x8c9daebf
[0x90/2 + 1] = 0x8c9d,
[0x94/2] = 0x1516, // 94: 13141516 .word 0x13141516
[0x94/2 + 1] = 0x1314,
[0x98/2] = 0x0700, // 98: 00010700 .word 0x00010700
[0x98/2 + 1] = 0x0001,

View File

@ -0,0 +1,112 @@
/* @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 -S stm32l05x-nvm-prog-write.o | ./code-to-array.pl > stm32l05x-nvm-prog-write.stub"
End:
*/

View File

@ -0,0 +1,201 @@
//
// stm32l05x-nvm-prog-write.o: file format elf32-littlearm
//
//
// Disassembly of section .text:
//
// 00000000 <stm32l05x_nvm_prog_write>:
// ".word 0\n\t"
// ".word 0\n\t"
// ".word 0\n\t"
// ".word 0\n\t"
// ".word 0\n\t"
// "0:");
[0x0/2] = 0xe00a, // 0: e00a b.n 18 <stm32l05x_nvm_prog_write+0x18>
[0x2/2] = 0x46c0, // 2: 46c0 nop ; (mov r8, r8)
// ...
// #define Nvm(nvm) (*reinterpret_cast<STM32::NVM*>(nvm))
// #define Info (*reinterpret_cast<stm32lx_nvm_stub_info*>(STM32Lx_STUB_INFO_PHYS))
//
// namespace {
// inline __attribute((always_inline)) bool unlock (STM32::NVM& nvm) {
// nvm.pecr = STM32Lx_NVM_PECR_PELOCK; // Lock to guarantee unlock
[0x18/2] = 0x2201, // 18: 2201 movs r2, #1
//
// auto& nvm = Nvm (Info.nvm);
[0x1a/2] = 0x4b2a, // 1a: 4b2a ldr r3, [pc, #168] ; (c4 <stm32l05x_nvm_prog_write+0xc4>)
[0x1c/2] = 0x68d9, // 1c: 68d9 ldr r1, [r3, #12]
[0x1e/2] = 0x604a, // 1e: 604a str r2, [r1, #4]
// nvm.pkeyr = STM32::NVM::PKEY1;
[0x20/2] = 0x4a29, // 20: 4a29 ldr r2, [pc, #164] ; (c8 <stm32l05x_nvm_prog_write+0xc8>)
[0x22/2] = 0x60ca, // 22: 60ca str r2, [r1, #12]
// nvm.pkeyr = STM32::NVM::PKEY2;
[0x24/2] = 0x4a29, // 24: 4a29 ldr r2, [pc, #164] ; (cc <stm32l05x_nvm_prog_write+0xcc>)
[0x26/2] = 0x60ca, // 26: 60ca str r2, [r1, #12]
// nvm.prgkeyr = STM32::NVM::PRGKEY1;
[0x28/2] = 0x4a29, // 28: 4a29 ldr r2, [pc, #164] ; (d0 <stm32l05x_nvm_prog_write+0xd0>)
[0x2a/2] = 0x610a, // 2a: 610a str r2, [r1, #16]
// nvm.prgkeyr = STM32::NVM::PRGKEY2;
[0x2c/2] = 0x4a29, // 2c: 4a29 ldr r2, [pc, #164] ; (d4 <stm32l05x_nvm_prog_write+0xd4>)
[0x2e/2] = 0x610a, // 2e: 610a str r2, [r1, #16]
// return !(nvm.pecr & STM32Lx_NVM_PECR_PRGLOCK);
[0x30/2] = 0x684a, // 30: 684a ldr r2, [r1, #4]
//
// if (!unlock (nvm))
[0x32/2] = 0x0792, // 32: 0792 lsls r2, r2, #30
[0x34/2] = 0xd502, // 34: d502 bpl.n 3c <stm32l05x_nvm_prog_write+0x3c>
// }
// inline __attribute((always_inline)) void lock (STM32::NVM& nvm) {
// nvm.pecr = STM32Lx_NVM_PECR_PELOCK; }
[0x36/2] = 0x2301, // 36: 2301 movs r3, #1
[0x38/2] = 0x604b, // 38: 604b str r3, [r1, #4]
// }
// }
//
// quit:
// lock (nvm);
// __asm volatile ("bkpt");
[0x3a/2] = 0xbe00, // 3a: be00 bkpt 0x0000
// auto& nvm = Nvm (Info.nvm);
//
// if (!unlock (nvm))
// goto quit;
//
// nvm.sr = STM32Lx_NVM_SR_ERR_M; // Clear errors
[0x3c/2] = 0x4826, // 3c: 4826 ldr r0, [pc, #152] ; (d8 <stm32l05x_nvm_prog_write+0xd8>)
[0x3e/2] = 0x6188, // 3e: 6188 str r0, [r1, #24]
//
// while (Info.size > 0) {
[0x40/2] = 0x685d, // 40: 685d ldr r5, [r3, #4]
[0x42/2] = 0x4e20, // 42: 4e20 ldr r6, [pc, #128] ; (c4 <stm32l05x_nvm_prog_write+0xc4>)
[0x44/2] = 0x2d00, // 44: 2d00 cmp r5, #0
[0x46/2] = 0xddf6, // 46: ddf6 ble.n 36 <stm32l05x_nvm_prog_write+0x36>
//
// // Either we're not half-page aligned or we have less than a half
// // page to write
// if (Info.size < Info.page_size/2
[0x48/2] = 0x8a32, // 48: 8a32 ldrh r2, [r6, #16]
[0x4a/2] = 0x0852, // 4a: 0852 lsrs r2, r2, #1
[0x4c/2] = 0x1e54, // 4c: 1e54 subs r4, r2, #1
[0x4e/2] = 0x4295, // 4e: 4295 cmp r5, r2
[0x50/2] = 0xdb02, // 50: db02 blt.n 58 <stm32l05x_nvm_prog_write+0x58>
// || (reinterpret_cast<uint32_t> (Info.destination)
[0x52/2] = 0x6837, // 52: 6837 ldr r7, [r6, #0]
[0x54/2] = 0x4227, // 54: 4227 tst r7, r4
[0x56/2] = 0xd01d, // 56: d01d beq.n 94 <stm32l05x_nvm_prog_write+0x94>
// & (Info.page_size/2 - 1))) {
// nvm.pecr = (Info.options & OPT_STM32L1) ? 0
// : STM32Lx_NVM_PECR_PROG; // Word programming
[0x58/2] = 0x2602, // 58: 2602 movs r6, #2
// // 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
[0x5a/2] = 0x8a5f, // 5a: 8a5f ldrh r7, [r3, #18]
// : STM32Lx_NVM_PECR_PROG; // Word programming
[0x5c/2] = 0x4037, // 5c: 4037 ands r7, r6
[0x5e/2] = 0x427e, // 5e: 427e negs r6, r7
[0x60/2] = 0x417e, // 60: 417e adcs r6, r7
[0x62/2] = 0x00f6, // 62: 00f6 lsls r6, r6, #3
[0x64/2] = 0x604e, // 64: 604e str r6, [r1, #4]
// size_t c = Info.page_size/2
// - (reinterpret_cast<uint32_t> (Info.destination)
// & (Info.page_size/2 - 1));
[0x66/2] = 0x681e, // 66: 681e ldr r6, [r3, #0]
[0x68/2] = 0x4034, // 68: 4034 ands r4, r6
[0x6a/2] = 0x1b12, // 6a: 1b12 subs r2, r2, r4
[0x6c/2] = 0x42aa, // 6c: 42aa cmp r2, r5
[0x6e/2] = 0xd900, // 6e: d900 bls.n 72 <stm32l05x_nvm_prog_write+0x72>
[0x70/2] = 0x1c2a, // 70: 1c2a adds r2, r5, #0
// if (c > Info.size)
// c = Info.size;
// Info.size -= c;
[0x72/2] = 0x1aad, // 72: 1aad subs r5, r5, r2
[0x74/2] = 0x605d, // 74: 605d str r5, [r3, #4]
// c /= 4;
[0x76/2] = 0x0892, // 76: 0892 lsrs r2, r2, #2
// while (c--) {
[0x78/2] = 0x3a01, // 78: 3a01 subs r2, #1
[0x7a/2] = 0xd3e1, // 7a: d3e1 bcc.n 40 <stm32l05x_nvm_prog_write+0x40>
// uint32_t v = *Info.source++;
[0x7c/2] = 0x689c, // 7c: 689c ldr r4, [r3, #8]
[0x7e/2] = 0x1d25, // 7e: 1d25 adds r5, r4, #4
[0x80/2] = 0x609d, // 80: 609d str r5, [r3, #8]
[0x82/2] = 0x6825, // 82: 6825 ldr r5, [r4, #0]
// *Info.destination++ = v;
[0x84/2] = 0x681c, // 84: 681c ldr r4, [r3, #0]
[0x86/2] = 0x1d26, // 86: 1d26 adds r6, r4, #4
[0x88/2] = 0x601e, // 88: 601e str r6, [r3, #0]
[0x8a/2] = 0x6025, // 8a: 6025 str r5, [r4, #0]
// if (nvm.sr & STM32Lx_NVM_SR_ERR_M)
[0x8c/2] = 0x698c, // 8c: 698c ldr r4, [r1, #24]
[0x8e/2] = 0x4204, // 8e: 4204 tst r4, r0
[0x90/2] = 0xd0f2, // 90: d0f2 beq.n 78 <stm32l05x_nvm_prog_write+0x78>
[0x92/2] = 0xe7d0, // 92: e7d0 b.n 36 <stm32l05x_nvm_prog_write+0x36>
// goto quit;
// }
// }
// // Or we are writing a half-page(s)
// else {
// nvm.pecr = STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_FPRG; // Half-page prg
[0x94/2] = 0x2481, // 94: 2481 movs r4, #129 ; 0x81
// size_t c = Info.size & ~(Info.page_size/2 - 1);
[0x96/2] = 0x4252, // 96: 4252 negs r2, r2
[0x98/2] = 0x402a, // 98: 402a ands r2, r5
// Info.size -= c;
[0x9a/2] = 0x1aad, // 9a: 1aad subs r5, r5, r2
// goto quit;
// }
// }
// // Or we are writing a half-page(s)
// else {
// nvm.pecr = STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_FPRG; // Half-page prg
[0x9c/2] = 0x00e4, // 9c: 00e4 lsls r4, r4, #3
[0x9e/2] = 0x604c, // 9e: 604c str r4, [r1, #4]
// size_t c = Info.size & ~(Info.page_size/2 - 1);
// Info.size -= c;
// c /= 4;
[0xa0/2] = 0x0892, // a0: 0892 lsrs r2, r2, #2
// }
// // 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;
[0xa2/2] = 0x6075, // a2: 6075 str r5, [r6, #4]
// c /= 4;
// while (c--) {
[0xa4/2] = 0x3a01, // a4: 3a01 subs r2, #1
[0xa6/2] = 0xd308, // a6: d308 bcc.n ba <stm32l05x_nvm_prog_write+0xba>
// uint32_t v = *Info.source++;
[0xa8/2] = 0x689c, // a8: 689c ldr r4, [r3, #8]
[0xaa/2] = 0x1d25, // aa: 1d25 adds r5, r4, #4
[0xac/2] = 0x609d, // ac: 609d str r5, [r3, #8]
[0xae/2] = 0x6825, // ae: 6825 ldr r5, [r4, #0]
// *Info.destination++ = v;
[0xb0/2] = 0x681c, // b0: 681c ldr r4, [r3, #0]
[0xb2/2] = 0x1d26, // b2: 1d26 adds r6, r4, #4
[0xb4/2] = 0x601e, // b4: 601e str r6, [r3, #0]
[0xb6/2] = 0x6025, // b6: 6025 str r5, [r4, #0]
[0xb8/2] = 0xe7f4, // b8: e7f4 b.n a4 <stm32l05x_nvm_prog_write+0xa4>
// }
// if (nvm.sr & STM32Lx_NVM_SR_ERR_M)
[0xba/2] = 0x698a, // ba: 698a ldr r2, [r1, #24]
[0xbc/2] = 0x4202, // bc: 4202 tst r2, r0
[0xbe/2] = 0xd0bf, // be: d0bf beq.n 40 <stm32l05x_nvm_prog_write+0x40>
[0xc0/2] = 0xe7b9, // c0: e7b9 b.n 36 <stm32l05x_nvm_prog_write+0x36>
[0xc2/2] = 0x46c0, // c2: 46c0 nop ; (mov r8, r8)
[0xc4/2] = 0x0004, // c4: 20000004 .word 0x20000004
[0xc4/2 + 1] = 0x2000,
[0xc8/2] = 0xcdef, // c8: 89abcdef .word 0x89abcdef
[0xc8/2 + 1] = 0x89ab,
[0xcc/2] = 0x0405, // cc: 02030405 .word 0x02030405
[0xcc/2 + 1] = 0x0203,
[0xd0/2] = 0xaebf, // d0: 8c9daebf .word 0x8c9daebf
[0xd0/2 + 1] = 0x8c9d,
[0xd4/2] = 0x1516, // d4: 13141516 .word 0x13141516
[0xd4/2 + 1] = 0x1314,
[0xd8/2] = 0x0700, // d8: 00010700 .word 0x00010700
[0xd8/2 + 1] = 0x0001,

View File

@ -41,6 +41,7 @@ SRC = \
samd.c \
stm32f1.c \
stm32f4.c \
stm32l0.c \
stm32l1.c \
swdptap.c \
target.c \

View File

@ -251,6 +251,7 @@ cortexm_probe(struct target_s *target)
PROBE(stm32f1_probe);
PROBE(stm32f4_probe);
PROBE(stm32l0_probe); /* STM32L0xx & STM32L1xx */
PROBE(stm32l1_probe);
PROBE(lpc11xx_probe);
PROBE(lpc43xx_probe);

201
src/include/stm32lx-nvm.h Normal file
View File

@ -0,0 +1,201 @@
/* @file stm32lx-nvm.h
*
* 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/>.
*/
#if !defined (STM32Lx_NVM_H_INCLUDED)
# define STM32Lx_NVM_H_INCLUDED
/* ----- Includes */
#include <stdint.h>
/* ----- Macros */
/* ----- Types */
enum {
STM32Lx_STUB_PHYS = 0x20000000ul,
STM32Lx_STUB_INFO_PHYS = 0x20000004ul,
STM32Lx_STUB_DATA_PHYS = (0x20000000ul + 1024),
STM32Lx_STUB_DATA_MAX = 2048,
STM32Lx_NVM_OPT_PHYS = 0x1ff80000ul,
STM32Lx_NVM_EEPROM_PHYS = 0x08080000ul,
STM32L0_NVM_PHYS = 0x40022000ul,
STM32L0_NVM_PROG_PAGE_SIZE = 128,
STM32L0_NVM_DATA_PAGE_SIZE = 4,
STM32L0_NVM_OPT_SIZE = 12,
STM32L0_NVM_EEPROM_SIZE = 2*1024,
STM32L1_NVM_PHYS = 0x40023c00ul,
STM32L1_NVM_PROG_PAGE_SIZE = 256,
STM32L1_NVM_DATA_PAGE_SIZE = 4,
STM32L1_NVM_OPT_SIZE = 32,
STM32L1_NVM_EEPROM_SIZE = 16*1024,
STM32Lx_NVM_PEKEY1 = 0x89abcdeful,
STM32Lx_NVM_PEKEY2 = 0x02030405ul,
STM32Lx_NVM_PRGKEY1 = 0x8c9daebful,
STM32Lx_NVM_PRGKEY2 = 0x13141516ul,
STM32Lx_NVM_OPTKEY1 = 0xfbead9c8ul,
STM32Lx_NVM_OPTKEY2 = 0x24252627ul,
STM32Lx_NVM_PECR_OBL_LAUNCH = (1<<18),
STM32Lx_NVM_PECR_ERRIE = (1<<17),
STM32Lx_NVM_PECR_EOPIE = (1<<16),
STM32Lx_NVM_PECR_FPRG = (1<<10),
STM32Lx_NVM_PECR_ERASE = (1<< 9),
STM32Lx_NVM_PECR_FIX = (1<< 8), /* FTDW */
STM32Lx_NVM_PECR_DATA = (1<< 4),
STM32Lx_NVM_PECR_PROG = (1<< 3),
STM32Lx_NVM_PECR_OPTLOCK = (1<< 2),
STM32Lx_NVM_PECR_PRGLOCK = (1<< 1),
STM32Lx_NVM_PECR_PELOCK = (1<< 0),
STM32Lx_NVM_SR_FWWERR = (1<<17),
STM32Lx_NVM_SR_NOTZEROERR = (1<<16),
STM32Lx_NVM_SR_RDERR = (1<<13),
STM32Lx_NVM_SR_OPTVER = (1<<11),
STM32Lx_NVM_SR_SIZERR = (1<<10),
STM32Lx_NVM_SR_PGAERR = (1<<9),
STM32Lx_NVM_SR_WRPERR = (1<<8),
STM32Lx_NVM_SR_READY = (1<<3),
STM32Lx_NVM_SR_HWOFF = (1<<2),
STM32Lx_NVM_SR_EOP = (1<<1),
STM32Lx_NVM_SR_BSY = (1<<0),
STM32Lx_NVM_SR_ERR_M = ( STM32Lx_NVM_SR_WRPERR
| STM32Lx_NVM_SR_PGAERR
| STM32Lx_NVM_SR_SIZERR
| STM32Lx_NVM_SR_NOTZEROERR),
STM32L0_NVM_OPTR_BOOT1 = (1<<31),
STM32L0_NVM_OPTR_WDG_SW = (1<<20),
STM32L0_NVM_OPTR_WPRMOD = (1<<8),
STM32L0_NVM_OPTR_RDPROT_S = (0),
STM32L0_NVM_OPTR_RDPROT_M = (0xff),
STM32L0_NVM_OPTR_RDPROT_0 = (0xaa),
STM32L0_NVM_OPTR_RDPROT_2 = (0xcc),
STM32L1_NVM_OPTR_nBFB2 = (1<<23),
STM32L1_NVM_OPTR_nRST_STDBY = (1<<22),
STM32L1_NVM_OPTR_nRST_STOP = (1<<21),
STM32L1_NVM_OPTR_WDG_SW = (1<<20),
STM32L1_NVM_OPTR_BOR_LEV_S = (16),
STM32L1_NVM_OPTR_BOR_LEV_M = (0xf),
STM32L1_NVM_OPTR_SPRMOD = (1<<8),
STM32L1_NVM_OPTR_RDPROT_S = (0),
STM32L1_NVM_OPTR_RDPROT_M = (0xff),
STM32L1_NVM_OPTR_RDPROT_0 = (0xaa),
STM32L1_NVM_OPTR_RDPROT_2 = (0xcc),
};
#if defined (__cplusplus)
namespace STM32 {
struct NVM {
volatile uint32_t acr;
volatile uint32_t pecr;
volatile uint32_t pdkeyr;
volatile uint32_t pkeyr;
volatile uint32_t prgkeyr;
volatile uint32_t optkeyr;
volatile uint32_t sr;
volatile uint32_t optr;
volatile uint32_t wrprot;
static constexpr uint32_t PKEY1 = 0x89abcdef;
static constexpr uint32_t PKEY2 = 0x02030405;
static constexpr uint32_t PRGKEY1 = 0x8c9daebf;
static constexpr uint32_t PRGKEY2 = 0x13141516;
static constexpr uint32_t OPTKEY1 = 0xfbead9c8;
static constexpr uint32_t OPTKEY2 = 0x24252627;
static constexpr uint32_t PDKEY1 = 0x04152637;
static constexpr uint32_t PDKEY2 = 0xfafbfcfd;
};
static_assert(sizeof (NVM) == 9*4, "NVM size error");
}
using stm32lx_stub_pointer_t = uint32_t*;
#define Nvm(nvm) (*reinterpret_cast<STM32::NVM*>(nvm))
#define Info (*reinterpret_cast<stm32lx_nvm_stub_info*>(STM32Lx_STUB_INFO_PHYS))
namespace {
inline __attribute((always_inline)) bool unlock (STM32::NVM& nvm) {
nvm.pecr = STM32Lx_NVM_PECR_PELOCK; // Lock to guarantee unlock
nvm.pkeyr = STM32::NVM::PKEY1;
nvm.pkeyr = STM32::NVM::PKEY2;
nvm.prgkeyr = STM32::NVM::PRGKEY1;
nvm.prgkeyr = STM32::NVM::PRGKEY2;
return !(nvm.pecr & STM32Lx_NVM_PECR_PRGLOCK);
}
inline __attribute((always_inline)) void lock (STM32::NVM& nvm) {
nvm.pecr = STM32Lx_NVM_PECR_PELOCK; }
}
#else
typedef uint32_t stm32lx_stub_pointer_t;
struct stm32lx_nvm {
volatile uint32_t acr;
volatile uint32_t pecr;
volatile uint32_t pdkeyr;
volatile uint32_t pekeyr;
volatile uint32_t prgkeyr;
volatile uint32_t optkeyr;
volatile uint32_t sr;
volatile uint32_t optr; /* or obr */
volatile uint32_t wrprot; /* or wprot1 */
};
#define STM32Lx_NVM(p) (*(struct stm32lx_nvm*) (p))
#define STM32Lx_NVM_PECR(p) ((uint32_t) &STM32Lx_NVM(p).pecr)
#define STM32Lx_NVM_PEKEYR(p) ((uint32_t) &STM32Lx_NVM(p).pekeyr)
#define STM32Lx_NVM_PRGKEYR(p) ((uint32_t) &STM32Lx_NVM(p).prgkeyr)
#define STM32Lx_NVM_OPTKEYR(p) ((uint32_t) &STM32Lx_NVM(p).optkeyr)
#define STM32Lx_NVM_SR(p) ((uint32_t) &STM32Lx_NVM(p).sr)
#define STM32Lx_NVM_OPTR(p) ((uint32_t) &STM32Lx_NVM(p).optr)
#endif
enum {
OPT_STM32L1 = 1<<1,
};
struct stm32lx_nvm_stub_info {
stm32lx_stub_pointer_t destination;
int32_t size;
stm32lx_stub_pointer_t source;
uint32_t nvm;
uint16_t page_size;
uint16_t options;
} __attribute__((packed));
/* ----- Globals */
/* ----- Prototypes */
#endif /* STM32Lx_NVM_H_INCLUDED */

View File

@ -216,6 +216,7 @@ void target_add_commands(target *t, const struct command_s *cmds, const char *na
bool cortexm_probe(struct target_s *target);
bool stm32f1_probe(struct target_s *target);
bool stm32f4_probe(struct target_s *target);
bool stm32l0_probe(struct target_s *target);
bool stm32l1_probe(struct target_s *target);
bool lmi_probe(struct target_s *target);
bool lpc11xx_probe(struct target_s *target);

1076
src/stm32l0.c Normal file

File diff suppressed because it is too large Load Diff