Browse Source

Revisions on Gareth's comments.

o Implemented byte writes to EEPROM now that the emulator has a
  byte-wide target write.
o Added comment describing the reason that mass erase doesn't work.
o Removed all unused code.
o Changed to Linux kernel indent style.
o Changed to Linux kernel function to parenthesis style.
o Stub generation doesn't use Perl, switched to sed.  Also, only
  including the instructions instead of the source and the instructions.
o Handling unaligned destination writes.
pull/75/head
Marc Singer 10 years ago
parent
commit
bf1cb71eb7
  1. 6
      flashstub/README
  2. 24
      flashstub/code-to-array.pl
  3. 11
      flashstub/dump-to-array.sh
  4. 2
      flashstub/stm32l05x-nvm-prog-erase.cc
  5. 226
      flashstub/stm32l05x-nvm-prog-erase.stub
  6. 9
      flashstub/stm32l05x-nvm-prog-write.cc
  7. 300
      flashstub/stm32l05x-nvm-prog-write.stub
  8. 4
      src/include/stm32lx-nvm.h
  9. 262
      src/stm32l0.c

6
flashstub/README

@ -11,3 +11,9 @@ 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.
The dump-to-array.sh helper script uses sed to transform the output of
'objdump -d' into a half-word array of the instructions that may be
included in C code to declare the stub. FWIW, objcopy doesn't produce
the same output as objdump. It omits some of the instructions,
probably because the object file isn't linked.

24
flashstub/code-to-array.pl

@ -1,24 +0,0 @@
#!/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 "// ", $_;
}
}

11
flashstub/dump-to-array.sh

@ -0,0 +1,11 @@
#!/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,/"

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

@ -87,7 +87,7 @@ quit:
/*
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"
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:
*/

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

@ -1,159 +1,67 @@
//
// 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,
[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,

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

@ -63,8 +63,8 @@ extern "C" void __attribute((naked)) stm32l05x_nvm_prog_write () {
while (Info.size > 0) {
// Either we're not half-page aligned or we have less than a half
// page to write
// 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))) {
@ -86,7 +86,8 @@ extern "C" void __attribute((naked)) stm32l05x_nvm_prog_write () {
}
// Or we are writing a half-page(s)
else {
nvm.pecr = STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_FPRG; // Half-page prg
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;
@ -106,7 +107,7 @@ quit:
/*
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"
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:
*/

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

@ -1,201 +1,99 @@
//
// 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,
[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,

4
src/include/stm32lx-nvm.h

@ -141,7 +141,9 @@ using stm32lx_stub_pointer_t = uint32_t*;
namespace {
inline __attribute((always_inline)) bool unlock (STM32::NVM& nvm) {
nvm.pecr = STM32Lx_NVM_PECR_PELOCK; // Lock to guarantee unlock
// Lock guarantees unlock
nvm.pecr = STM32Lx_NVM_PECR_PELOCK;
nvm.pkeyr = STM32::NVM::PKEY1;
nvm.pkeyr = STM32::NVM::PKEY2;
nvm.prgkeyr = STM32::NVM::PRGKEY1;

262
src/stm32l0.c

@ -52,18 +52,34 @@
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.
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 broken. 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 bytes
again. The command fails because we lose contact with the target
when we perform the option byte reload. For the time being, the
command is disabled.
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
bytes again. The command fails because we lose contact with the
target when we perform the option byte reload. For the time
being, the command is disabled.
The body of the function was the following. It is left here for
reference in case someone either discovers what is wrong with
these lines, or a change is made to the emulator that allows it
to regain control of the target after the option byte reload.
stm32l0_option_write(t, 0x1ff80000, 0xffff0000);
adiv5_ap_mem_write(ap, STM32L0_NVM_PECR, STM32L0_NVM_PECR_OBL_LAUNCH);
stm32l0_option_write(t, 0x1ff80000, 0xff5500aa);
adiv5_ap_mem_write(ap, STM32L0_NVM_PECR, STM32L0_NVM_PECR_OBL_LAUNCH);
uint32_t sr;
do {
sr = adiv5_ap_mem_read(ap, STM32L0_NVM_SR);
} while (sr & STM32L0_NVM_SR_BSY);
o Errors. We probably should clear SR errors immediately after
detecting them. If we don't then we always must wait for the NVM
@ -75,45 +91,24 @@
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.
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 suspect
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.
0x1ff80000: 0x00aa 0xff55 OK
0x1ff80004: 0x8070 0x7f8f OK
0x1ff80008: 0x0000 0xffff OK
OPTR: 0x807000aa, RDPROT 0, WPRMOD 0, WDG_SW 1, BOOT1 1
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.
Options code
p *(int*)0x40022004 = 1
p *(int*)0x4002200c = 0x89abcdef
p *(int*)0x4002200c = 0x02030405
p *(int*)0x40022014 = 0xfbead9c8
p *(int*)0x40022014 = 0x24252627
p *(int*)0x40022004 = 0x10
x/4wx 0x40022000
p *(int*)0x1ff80000 = 0xff5500aa
l1 program
p *(int*)0x40023c04 = 1
p *(int*)0x40023c0c = 0x89abcdef
p *(int*)0x40023c0c = 0x02030405
p *(int*)0x40023c10 = 0x8c9daebf
p *(int*)0x40023c10 = 0x13141516
p *(int*)0x40023c14 = 0xfbead9c8
p *(int*)0x40023c14 = 0x24252627
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.
*/
@ -130,7 +125,7 @@ p *(int*)0x40023c14 = 0x24252627
#include "stm32lx-nvm.h"
static int inhibit_stubs;
static int inhibit_stubs; /* Local option to force non-stub flash IO */
static int stm32lx_nvm_erase(struct target_s* target,
uint32_t addr, int len);
@ -160,18 +155,13 @@ static int stm32lx_nvm_data_write (struct target_s* target,
const uint8_t* source,
int size);
//static bool stm32l0_cmd_erase_mass (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 bool stm32l0_cmd_reset (target* t, int argc, char** argv);
static bool stm32lx_cmd_stubs (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" },
// { "erase_mass", (cmd_handler) stm32l0_cmd_erase_mass,
// "Erase NVM flash and data" },
// { "reset", (cmd_handler) stm32l0_cmd_reset, "Reset target" },
{ "option", (cmd_handler) stm32lx_cmd_option,
"Manipulate option bytes"},
{ "eeprom", (cmd_handler) stm32lx_cmd_eeprom,
@ -338,12 +328,14 @@ bool stm32l0_probe (struct target_s* target)
return false;
}
/** Lock the NVM control registers preventing writes or erases. */
static void stm32lx_nvm_lock(ADIv5_AP_t* ap, uint32_t nvm)
{
adiv5_ap_mem_write(ap, STM32Lx_NVM_PECR(nvm), STM32Lx_NVM_PECR_PELOCK);
}
/** Unlock the NVM control registers for modifying program or
data flash. Returns true if the unlock succeeds. */
static bool stm32lx_nvm_prog_data_unlock(ADIv5_AP_t* ap, uint32_t nvm)
@ -412,7 +404,8 @@ static int stm32lx_nvm_prog_erase_stubbed (struct target_s* target,
;
{
ADIv5_AP_t* ap = adiv5_target_ap(target);
if (adiv5_ap_mem_read (ap, STM32Lx_NVM_SR(nvm)) & STM32Lx_NVM_SR_ERR_M)
if (adiv5_ap_mem_read(ap, STM32Lx_NVM_SR(nvm))
& STM32Lx_NVM_SR_ERR_M)
return -1;
}
@ -433,13 +426,10 @@ static int stm32lx_nvm_prog_write_stubbed (struct target_s* target,
const uint32_t nvm = stm32lx_nvm_phys(target);
const size_t page_size = stm32lx_nvm_prog_page_size(target);
/* We can't handle unaligned destination or non-word writes. */
/* *** FIXME: we should handle misaligned writes by padding with
*** zeros. Probably, the only real time we'd see something
*** misaligned would be on a write to a final half-word. Perhaps
*** this could be handled with the stub? In fact, aligning the
*** start is going to be mandatory. We will change the code to
*** cope with a trailing half-word. */
/* 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;
@ -454,11 +444,13 @@ static int stm32lx_nvm_prog_write_stubbed (struct target_s* target,
while (size > 0) {
/* Max transfer size is adjusted in the event that the
destination isn't half-page aligned. This allows the
sub to write the first partial half-page and then
as many half-pages as will fit in the buffer. */
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)));
- (destination - (destination
& ~(info.page_size/2 - 1)));
size_t cb = size;
if (cb > max)
cb = max;
@ -469,7 +461,8 @@ static int stm32lx_nvm_prog_write_stubbed (struct target_s* target,
info.size = cb;
/* Copy data to write to flash */
target_mem_write_words (target, info.source, (void*) source, info.size);
target_mem_write_words(target, info.source, (void*) source,
info.size);
/* Move pointers early */
destination += cb;
@ -488,7 +481,8 @@ static int stm32lx_nvm_prog_write_stubbed (struct target_s* target,
while (!target_halt_wait(target))
;
if (adiv5_ap_mem_read (adiv5_target_ap (target), STM32Lx_NVM_SR(nvm))
if (adiv5_ap_mem_read(adiv5_target_ap(target),
STM32Lx_NVM_SR(nvm))
& STM32Lx_NVM_SR_ERR_M)
return -1;
}
@ -516,25 +510,47 @@ static int stm32lx_nvm_erase (struct target_s* target, uint32_t addr, int size)
}
/** Write to a region on NVM for STM32L1xx. This is the lead function
and it will ibvoke an implementation, stubbed or not depending on
the options and the range of addresses. */
/** 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(struct target_s* target,
uint32_t destination,
const uint8_t* source,
int size)
{
if (destination >= STM32Lx_NVM_EEPROM_PHYS)
return stm32lx_nvm_data_write (target, destination, source, size);
return stm32lx_nvm_data_write(target, 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. */
/* 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(target, &regs);
if (inhibit_stubs || (regs[16] & 0xf))
return stm32lx_nvm_prog_write (target, destination, source, size);
return stm32lx_nvm_prog_write(target, destination, source,
size);
return stm32lx_nvm_prog_write_stubbed (target, destination, source, size);
return stm32lx_nvm_prog_write_stubbed(target, destination, source,
size);
}
@ -610,9 +626,10 @@ static int stm32lx_nvm_prog_write (struct target_s* target,
const uint32_t nvm = stm32lx_nvm_phys(target);
const bool is_stm32l1 = stm32lx_is_stm32l1(target);
/* We can only handle word aligned writes and even word-multiple
ranges. The stm32l0 cannot perform anything smaller than a word
write due to the ECC bits. */
/* 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;
@ -625,18 +642,23 @@ static int stm32lx_nvm_prog_write (struct target_s* target,
while (size > 0) {
/* Wait for BSY to clear because we cannot write the PECR until
the previous operation completes on STM32L1xx. */
while (adiv5_ap_mem_read (ap, STM32Lx_NVM_SR(nvm)) & STM32Lx_NVM_SR_BSY)
the previous operation completes on STM32Lxxx. */
while (adiv5_ap_mem_read(ap, STM32Lx_NVM_SR(nvm))
& STM32Lx_NVM_SR_BSY)
if (target_check_error(target)) {
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))) {
// 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))) {
adiv5_ap_mem_write(ap, STM32Lx_NVM_PECR(nvm),
is_stm32l1 ? 0 : STM32Lx_NVM_PECR_PROG);
size_t c = half_page_size - (destination & (half_page_size - 1));
is_stm32l1
? 0
: STM32Lx_NVM_PECR_PROG);
size_t c = half_page_size - (destination
& (half_page_size - 1));
if (c > size)
c = size;
@ -649,7 +671,8 @@ static int stm32lx_nvm_prog_write (struct target_s* target,
// Or we are writing a half-page(s)
else {
adiv5_ap_mem_write(ap, STM32Lx_NVM_PECR(nvm),
STM32Lx_NVM_PECR_PROG | STM32Lx_NVM_PECR_FPRG);
STM32Lx_NVM_PECR_PROG
| STM32Lx_NVM_PECR_FPRG);
size_t c = size & ~(half_page_size - 1);
size -= c;
@ -737,10 +760,9 @@ static int stm32lx_nvm_data_erase (struct target_s* target,
/** Write to data flash using operations through the debug interface.
NVM register file address chosen from target.
*** FIXME: need to make this work with writing a single byte as
well as words. */
NVM register file address chosen from target. Unaligned
destination writes are supported (though unaligned sources are
not). */
static int stm32lx_nvm_data_write(struct target_s* target,
uint32_t destination,
const uint8_t* source_8,
@ -749,15 +771,11 @@ static int stm32lx_nvm_data_write (struct target_s* target,
ADIv5_AP_t* ap = adiv5_target_ap(target);
const uint32_t nvm = stm32lx_nvm_phys(target);
const bool is_stm32l1 = stm32lx_is_stm32l1(target);
/* *** FIXME: need to make this work with writing a single byte. */
if ((destination & 3) || (size & 3))
return -1;
uint32_t* source = (uint32_t*) source_8;
if (!stm32lx_nvm_prog_data_unlock(ap, nvm))
return -1;
uint32_t* source = (uint32_t*) source_8;
adiv5_ap_mem_write(ap, STM32Lx_NVM_PECR(nvm),
is_stm32l1 ? 0 : STM32Lx_NVM_PECR_DATA);
@ -840,6 +858,8 @@ static bool stm32lx_eeprom_write (target *t, uint32_t address,
adiv5_ap_mem_write(ap, address, value);
else if (cb == 2)
adiv5_ap_mem_write_halfword(ap, address, value);
else if (cb == 1)
adiv5_ap_mem_write_byte(ap, address, value);
else
return false;
@ -854,6 +874,7 @@ static bool stm32lx_eeprom_write (target *t, uint32_t address,
static bool stm32lx_cmd_stubs(target* t,
int argc, char** argv)
{
(void) t;
if (argc == 1) {
gdb_out("usage: mon stubs [enable/disable]\n");
}
@ -869,38 +890,6 @@ static bool stm32lx_cmd_stubs (target* t,
return true;
}
#if 0
static bool stm32l0_cmd_erase_mass (target* t, int argc , char** argv)
{
ADIv5_AP_t* ap = adiv5_target_ap (t);
stm32lx_nvm_opt_unlock (ap);
stm32l0_option_write (t, 0x1ff80000, 0xffff0000);
adiv5_ap_mem_write (ap, STM32L0_NVM_PECR, STM32L0_NVM_PECR_OBL_LAUNCH);
stm32l0_option_write (t, 0x1ff80000, 0xff5500aa);
adiv5_ap_mem_write (ap, STM32L0_NVM_PECR, STM32L0_NVM_PECR_OBL_LAUNCH);
uint32_t sr;
do {
sr = adiv5_ap_mem_read (ap, STM32L0_NVM_SR);
} while (sr & STM32L0_NVM_SR_BSY);
stm32l0_nvm_lock (ap);
return true;
}
#endif
#if 0
static bool stm32l0_cmd_reset (target* t, int argc, char** argv)
{
gdb_out ("Resetting target\n");
target_reset (t);
return true;
}
#endif
static bool stm32lx_cmd_option(target* t, int argc, char** argv)
{
ADIv5_AP_t* ap = adiv5_target_ap(t);
@ -915,7 +904,8 @@ static bool stm32lx_cmd_option (target* t, int argc, char** argv)
size_t cb = strlen(argv[1]);
if (argc == 2 && !strncasecmp(argv[1], "obl_launch", cb)) {
adiv5_ap_mem_write (ap, STM32Lx_NVM_PECR(nvm), STM32Lx_NVM_PECR_OBL_LAUNCH);
adiv5_ap_mem_write(ap, STM32Lx_NVM_PECR(nvm),
STM32Lx_NVM_PECR_OBL_LAUNCH);
}
else if (argc == 4 && !strncasecmp(argv[1], "raw", cb)) {
uint32_t addr = strtoul(argv[2], NULL, 0);
@ -951,7 +941,8 @@ static bool stm32lx_cmd_option (target* t, int argc, char** argv)
uint32_t val = adiv5_ap_mem_read(ap, addr);
gdb_outf("0x%08x: 0x%04x 0x%04x %s\n",
addr, val & 0xffff, (val >> 16) & 0xffff,
((val & 0xffff) == ((~val >> 16) & 0xffff)) ? "OK" : "ERR");
((val & 0xffff) == ((~val >> 16) & 0xffff))
? "OK" : "ERR");
}
if (stm32lx_is_stm32l1(t)) {
@ -965,10 +956,12 @@ static bool stm32lx_cmd_option (target* t, int argc, char** argv)
else
rdprot = 1;
gdb_outf("OPTR: 0x%08x, RDPRT %d, SPRMD %d, "
"BOR %d, WDG_SW %d, nRST_STP %d, nRST_STBY %d, nBFB2 %d\n",
"BOR %d, WDG_SW %d, nRST_STP %d, nRST_STBY %d, "
"nBFB2 %d\n",
optr, rdprot,
(optr & STM32L1_NVM_OPTR_SPRMOD) ? 1 : 0,
(optr >> STM32L1_NVM_OPTR_BOR_LEV_S) & STM32L1_NVM_OPTR_BOR_LEV_M,
(optr >> STM32L1_NVM_OPTR_BOR_LEV_S)
& STM32L1_NVM_OPTR_BOR_LEV_M,
(optr & STM32L1_NVM_OPTR_WDG_SW) ? 1 : 0,
(optr & STM32L1_NVM_OPTR_nRST_STOP) ? 1 : 0,
(optr & STM32L1_NVM_OPTR_nRST_STDBY) ? 1 : 0,
@ -984,7 +977,8 @@ static bool stm32lx_cmd_option (target* t, int argc, char** argv)
rdprot = 2;
else
rdprot = 1;
gdb_outf ("OPTR: 0x%08x, RDPROT %d, WPRMOD %d, WDG_SW %d, BOOT1 %d\n",
gdb_outf("OPTR: 0x%08x, RDPROT %d, WPRMOD %d, WDG_SW %d, "
"BOOT1 %d\n",
optr, rdprot,
(optr & STM32L0_NVM_OPTR_WPRMOD) ? 1 : 0,
(optr & STM32L0_NVM_OPTR_WDG_SW) ? 1 : 0,
@ -995,7 +989,8 @@ static bool stm32lx_cmd_option (target* t, int argc, char** argv)
usage:
gdb_out("usage: monitor option [ARGS]\n");
gdb_out (" show - Show options in NVM and as loaded\n");
gdb_out(" show - Show options in NVM and as"
" loaded\n");
gdb_out(" obl_launch - Reload options from NVM\n");
gdb_out(" write <addr> <value16> - Set option half-word; "
"complement computed\n");
@ -1028,19 +1023,18 @@ static bool stm32lx_cmd_eeprom (target* t, int argc, char** argv)
uint32_t val = strtoul(argv[3], NULL, 0);
if ( addr < STM32Lx_NVM_EEPROM_PHYS
|| addr >= STM32Lx_NVM_EEPROM_PHYS + stm32lx_nvm_eeprom_size (t))
|| addr >= STM32Lx_NVM_EEPROM_PHYS
+ stm32lx_nvm_eeprom_size(t))
goto usage;
#if 0
if (!strncasecmp(argv[1], "byte", cb)) {
gdb_outf("write byte 0x%08x <- 0x%08x\n", addr, val);
if (!stm32l0_eeprom_write (t, addr, 1, val))
if (!stm32lx_eeprom_write(t, addr, 1, val))
gdb_out("eeprom write failed\n");
} else
#endif
if (!strncasecmp (argv[1], "halfword", cb)) {
} else if (!strncasecmp(argv[1], "halfword", cb)) {
val &= 0xffff;
gdb_outf ("write halfword 0x%08x <- 0x%04x\n", addr, val);
gdb_outf("write halfword 0x%08x <- 0x%04x\n",
addr, val);
if (addr & 1)
goto usage;
if (!stm32lx_eeprom_write(t, addr, 2, val))
@ -1062,7 +1056,7 @@ static bool stm32lx_cmd_eeprom (target* t, int argc, char** argv)
usage:
gdb_out("usage: monitor eeprom [ARGS]\n");
// gdb_out (" byte <addr> <value8> - Write a byte\n");
gdb_out(" byte <addr> <value8> - Write a byte\n");
gdb_out(" halfword <addr> <value16> - Write a half-word\n");
gdb_out(" word <addr> <value32> - Write a word\n");
gdb_outf("The value of <addr> must in the interval [0x%08x, 0x%x)\n",

Loading…
Cancel
Save