Browse Source

Merge pull request #87 from gsmcmullin/new_flash

Rework flash memory driver interface.
pull/91/head
Gareth McMullin 10 years ago
parent
commit
492d6c9cf8
  1. 11
      flashstub/dump-to-array.sh
  2. 49
      flashstub/lmi.c
  3. 42
      flashstub/lmi.s
  4. 2
      flashstub/lmi.stub
  5. 93
      flashstub/stm32l05x-nvm-prog-erase.cc
  6. 67
      flashstub/stm32l05x-nvm-prog-erase.stub
  7. 113
      flashstub/stm32l05x-nvm-prog-write.cc
  8. 99
      flashstub/stm32l05x-nvm-prog-write.stub
  9. 1
      src/Makefile
  10. 14
      src/cortexm.c
  11. 12
      src/include/cortexm.h
  12. 68
      src/include/lpc_common.h
  13. 61
      src/include/target.h
  14. 65
      src/kinetis.c
  15. 72
      src/lmi.c
  16. 291
      src/lpc11xx.c
  17. 417
      src/lpc43xx.c
  18. 135
      src/lpc_common.c
  19. 69
      src/nrf51.c
  20. 1
      src/platforms/native/platform.h
  21. 387
      src/sam3x.c
  22. 111
      src/samd.c
  23. 139
      src/stm32f1.c
  24. 118
      src/stm32f4.c
  25. 723
      src/stm32l0.c
  26. 176
      src/target.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,/"

49
flashstub/lmi.c

@ -0,0 +1,49 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2015 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* 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/>.
*/
#include <stdint.h>
#include "stub.h"
#define LMI_FLASH_BASE ((volatile uint32_t *)0x400FD000)
#define LMI_FLASH_FMA LMI_FLASH_BASE[0]
#define LMI_FLASH_FMD LMI_FLASH_BASE[1]
#define LMI_FLASH_FMC LMI_FLASH_BASE[2]
#define LMI_FLASH_FMC_WRITE (1 << 0)
#define LMI_FLASH_FMC_ERASE (1 << 1)
#define LMI_FLASH_FMC_MERASE (1 << 2)
#define LMI_FLASH_FMC_COMT (1 << 3)
#define LMI_FLASH_FMC_WRKEY 0xA4420000
void __attribute__((naked))
stm32f1_flash_write_stub(uint32_t *dest, uint32_t *src, uint32_t size)
{
size /= 4;
for (int i; i < size; i++) {
LMI_FLASH_FMA = (uint32_t)&dest[i];
LMI_FLASH_FMD = src[i];
LMI_FLASH_FMC = LMI_FLASH_FMC_WRKEY | LMI_FLASH_FMC_WRITE;
while (LMI_FLASH_FMC & LMI_FLASH_FMC_WRITE)
;
}
stub_exit(0);
}

42
flashstub/lmi.s

@ -1,42 +0,0 @@
_start:
ldr r0, _flashbase
ldr r1, _addr
mov r2, pc
add r2, #(_data - . - 2)
ldr r3, _size
ldr r5, _flash_write_cmd
_next:
cbz r3, _done
@ Write address to FMA
str r1, [r0]
@ Write data to FMD
ldr r4, [r2]
str r4, [r0, #4]
@ Write WRITE bit to FMC
str r5, [r0, #8]
_wait: @ Wait for WRITE bit to clear
ldr r4, [r0, #8]
mov r6, #1
tst r4, r6
bne _wait
sub r3, #1
add r1, #4
add r2, #4
b _next
_done:
bkpt
@.align 4
.org 0x28
_flashbase:
.word 0x400FD000
_flash_write_cmd:
.word 0xA4420001
_addr:
.word 0
_size:
.word 4
_data:
.string "Hello World!\n\0\0\0"

2
flashstub/lmi.stub

@ -1 +1 @@
0x4809, 0x490B, 0x467A, 0x3230, 0x4B0A, 0x4D08, 0xB15B, 0x6001, 0x6814, 0x6044, 0x6085, 0x6884, 0x2601, 0x4234, 0xD1FB, 0x3B01, 0x3104, 0x3204, 0xE7F2, 0xBE00, 0xD000, 0x400F, 0x0001, 0xA442, 0x0000, 0x0000, 0x0004, 0x0000, 0x6548, 0x6C6C, 0x206F, 0x6F57, 0x6C72, 0x2164, 0x000A, 0x0000, 0x0000,
0x0892, 0x2300, 0x4293, 0xD20F, 0x4C08, 0x6020, 0xF851, 0x5023, 0x3408, 0xF844, 0x5C04, 0x4D06, 0x6025, 0x6825, 0xF015, 0x0F01, 0xD1FB, 0x3301, 0x3004, 0xE7ED, 0xBE00, 0xBF00, 0xD000, 0x400F, 0x0001, 0xA442,

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,

1
src/Makefile

@ -28,6 +28,7 @@ SRC = \
jtag_scan.c \
jtagtap.c \
lmi.c \
lpc_common.c \
lpc11xx.c \
lpc43xx.c \
kinetis.c \

14
src/cortexm.c

@ -195,14 +195,6 @@ static const char tdesc_cortex_mf[] =
" </feature>"
"</target>";
#define REG_SP 13
#define REG_LR 14
#define REG_PC 15
#define REG_XPSR 16
#define REG_MSP 17
#define REG_PSP 18
#define REG_SPECIAL 19
bool cortexm_probe(target *t)
{
t->driver = cortexm_driver_str;
@ -426,8 +418,10 @@ static void cortexm_pc_write(target *t, const uint32_t val)
* using the core debug registers in the NVIC. */
static void cortexm_reset(target *t)
{
jtagtap_srst(true);
jtagtap_srst(false);
if ((t->target_options & CORTEXM_TOPT_INHIBIT_SRST) == 0) {
jtagtap_srst(true);
jtagtap_srst(false);
}
/* Read DHCSR here to clear S_RESET_ST bit before reset */
target_mem_read32(t, CORTEXM_DHCSR);

12
src/include/cortexm.h

@ -143,6 +143,18 @@
#define CORTEXM_DWT_FUNC_FUNC_WRITE (6 << 0)
#define CORTEXM_DWT_FUNC_FUNC_ACCESS (7 << 0)
#define REG_SP 13
#define REG_LR 14
#define REG_PC 15
#define REG_XPSR 16
#define REG_MSP 17
#define REG_PSP 18
#define REG_SPECIAL 19
#define ARM_THUMB_BREAKPOINT 0xBE00
#define CORTEXM_TOPT_INHIBIT_SRST (1 << 2)
bool cortexm_attach(target *t);
void cortexm_detach(target *t);
void cortexm_halt_resume(target *t, bool step);

68
src/include/lpc_common.h

@ -0,0 +1,68 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2015 Gareth McMullin <gareth@blacksphere.co.nz>
*
* 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/>.
*/
#ifndef __LPC_COMMON_H
#define __LPC_COMMON_H
enum iap_cmd {
IAP_CMD_INIT = 49,
IAP_CMD_PREPARE = 50,
IAP_CMD_PROGRAM = 51,
IAP_CMD_ERASE = 52,
IAP_CMD_BLANKCHECK = 53,
IAP_CMD_SET_ACTIVE_BANK = 60,
};
enum iap_status {
IAP_STATUS_CMD_SUCCESS = 0,
IAP_STATUS_INVALID_COMMAND = 1,
IAP_STATUS_SRC_ADDR_ERROR = 2,
IAP_STATUS_DST_ADDR_ERROR = 3,
IAP_STATUS_SRC_ADDR_NOT_MAPPED = 4,
IAP_STATUS_DST_ADDR_NOT_MAPPED = 5,
IAP_STATUS_COUNT_ERROR = 6,
IAP_STATUS_INVALID_SECTOR = 7,
IAP_STATUS_SECTOR_NOT_BLANK = 8,
IAP_STATUS_SECTOR_NOT_PREPARED = 9,
IAP_STATUS_COMPARE_ERROR = 10,
IAP_STATUS_BUSY = 11,
};
/* CPU Frequency */
#define CPU_CLK_KHZ 12000
struct lpc_flash {
struct target_flash f;
uint8_t base_sector;
uint8_t bank;
/* Info filled in by specific driver */
void (*wdt_kick)(target *t);
uint32_t iap_entry;
uint32_t iap_ram;
uint32_t iap_msp;
};
struct lpc_flash *lpc_add_flash(target *t, uint32_t addr, size_t length);
enum iap_status lpc_iap_call(struct lpc_flash *f, enum iap_cmd cmd, ...);
int lpc_flash_erase(struct target_flash *f, uint32_t addr, size_t len);
int lpc_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len);
#endif

61
src/include/target.h

@ -95,14 +95,10 @@ target *target_attach(target *t, target_destroy_callback destroy_cb);
/* Flash memory access functions */
#define target_flash_erase(target, addr, len) \
(target)->flash_erase((target), (addr), (len))
#define target_flash_write(target, dest, src, len) \
(target)->flash_write((target), (dest), (src), (len))
#define target_flash_done(target) \
((target)->flash_done ? (target)->flash_done(target) : 0)
int target_flash_erase(target *t, uint32_t addr, size_t len);
int target_flash_write(target *t,
uint32_t dest, const void *src, size_t len);
int target_flash_done(target *t);
/* Host I/O */
#define target_hostio_reply(target, recode, errcode) \
@ -112,12 +108,39 @@ target *target_attach(target *t, target_destroy_callback destroy_cb);
#define target_regs_size(target) \
((target)->regs_size)
#define target_mem_map(target) \
((target)->xml_mem_map ? (target)->xml_mem_map : "")
#define target_tdesc(target) \
((target)->tdesc ? (target)->tdesc : "")
struct target_ram {
uint32_t start;
uint32_t length;
struct target_ram *next;
};
struct target_flash;
typedef int (*flash_erase_func)(struct target_flash *f, uint32_t addr, size_t len);
typedef int (*flash_write_func)(struct target_flash *f, uint32_t dest,
const void *src, size_t len);
typedef int (*flash_done_func)(struct target_flash *f);
struct target_flash {
uint32_t start;
uint32_t length;
uint32_t blocksize;
flash_erase_func erase;
flash_write_func write;
flash_done_func done;
target *t;
struct target_flash *next;
int align;
uint8_t erased;
/* For buffered flash */
size_t buf_size;
flash_write_func write_buf;
uint32_t buf_addr;
void *buf;
};
struct target_s {
/* Notify controlling debugger if target is lost */
target_destroy_callback destroy_callback;
@ -158,12 +181,10 @@ struct target_s {
unsigned target_options;
uint32_t idcode;
/* Flash memory access functions */
const char *xml_mem_map;
int (*flash_erase)(target *t, uint32_t addr, size_t len);
int (*flash_write)(target *t, uint32_t dest,
const uint8_t *src, size_t len);
int (*flash_done)(target *t);
/* Target memory map */
char *dyn_mem_map;
struct target_ram *ram;
struct target_flash *flash;
/* Host I/O support */
void (*hostio_reply)(target *t, int32_t retcode, uint32_t errcode);
@ -190,6 +211,12 @@ extern bool connect_assert_srst;
target *target_new(unsigned size);
void target_list_free(void);
void target_add_commands(target *t, const struct command_s *cmds, const char *name);
void target_add_ram(target *t, uint32_t start, uint32_t len);
void target_add_flash(target *t, struct target_flash *f);
const char *target_mem_map(target *t);
int target_flash_write_buffered(struct target_flash *f,
uint32_t dest, const void *src, size_t len);
int target_flash_done_buffered(struct target_flash *f);
static inline uint32_t target_mem_read32(target *t, uint32_t addr)
{

65
src/kinetis.c

@ -20,10 +20,12 @@
/* This file implements KL25 target specific functions providing
* the XML memory map and Flash memory programming.
*
* According to Freescale doc KL25P80M48SF0RM:
* KL25 Sub-family Reference Manual
*/
#include "general.h"
#include "adiv5.h"
#include "target.h"
#define SIM_SDID 0x40048024
@ -54,21 +56,23 @@
#define KL25_PAGESIZE 0x400
static int kl25_flash_erase(target *t, uint32_t addr, size_t len);
static int kl25_flash_write(target *t, uint32_t dest,
const uint8_t *src, size_t len);
static const char kl25_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>"
" <memory type=\"flash\" start=\"0\" length=\"0x20000\">"
" <property name=\"blocksize\">0x400</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x1ffff000\" length=\"0x1000\"/>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x3000\"/>"
"</memory-map>";
static int kl25_flash_erase(struct target_flash *f, uint32_t addr, size_t len);
static int kl25_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len);
static void kl25_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 = kl25_flash_erase;
f->write = kl25_flash_write;
f->align = 4;
f->erased = 0xff;
target_add_flash(t, f);
}
bool kinetis_probe(target *t)
{
@ -76,9 +80,9 @@ bool kinetis_probe(target *t)
switch (sdid >> 20) {
case 0x251:
t->driver = "KL25";
t->xml_mem_map = kl25_xml_memory_map;
t->flash_erase = kl25_flash_erase;
t->flash_write = kl25_flash_write;
target_add_ram(t, 0x1ffff000, 0x1000);
target_add_ram(t, 0x20000000, 0x3000);
kl25_add_flash(t, 0x00000000, 0x20000, 0x400);
return true;
}
return false;
@ -89,11 +93,11 @@ kl25_command(target *t, uint8_t cmd, uint32_t addr, const uint8_t data[8])
{
uint8_t fstat;
/* Wait for CCIF to be high */
/* Wait for CCIF to be high */
do {
fstat = target_mem_read8(t, FTFA_FSTAT);
/* Check ACCERR and FPVIOL are zero in FSTAT */
if (fstat & (FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL))
if (fstat & (FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL))
return false;
} while (!(fstat & FTFA_FSTAT_CCIF));
@ -113,35 +117,28 @@ kl25_command(target *t, uint8_t cmd, uint32_t addr, const uint8_t data[8])
do {
fstat = target_mem_read8(t, FTFA_FSTAT);
/* Check ACCERR and FPVIOL are zero in FSTAT */
if (fstat & (FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL))
if (fstat & (FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL))
return false;
} while (!(fstat & FTFA_FSTAT_CCIF));
return true;
}
static int kl25_flash_erase(target *t, uint32_t addr, size_t len)
static int kl25_flash_erase(struct target_flash *f, uint32_t addr, size_t len)
{
addr &= ~(KL25_PAGESIZE - 1);
len = (len + KL25_PAGESIZE - 1) & ~(KL25_PAGESIZE - 1);
while (len) {
kl25_command(t, FTFA_CMD_ERASE_SECTOR, addr, NULL);
kl25_command(f->t, FTFA_CMD_ERASE_SECTOR, addr, NULL);
len -= KL25_PAGESIZE;
addr += KL25_PAGESIZE;
}
return 0;
}
static int kl25_flash_write(target *t, uint32_t dest,
const uint8_t *src, size_t len)
static int kl25_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len)
{
/* FIXME handle misaligned start and end of sections */
if ((dest & 3) || (len & 3))
return -1;
while (len) {
kl25_command(t, FTFA_CMD_PROGRAM_LONGWORD, dest, src);
kl25_command(f->t, FTFA_CMD_PROGRAM_LONGWORD, dest, src);
len -= 4;
dest += 4;
src += 4;

72
src/lmi.c

@ -29,7 +29,7 @@
#include "cortexm.h"
#define SRAM_BASE 0x20000000
#define STUB_BUFFER_BASE (SRAM_BASE + 0x30)
#define STUB_BUFFER_BASE ALIGN(SRAM_BASE + sizeof(lmi_flash_write_stub), 4)
#define BLOCK_SIZE 0x400
@ -46,65 +46,51 @@
#define LMI_FLASH_FMC_COMT (1 << 3)
#define LMI_FLASH_FMC_WRKEY 0xA4420000
static int lmi_flash_erase(target *t, uint32_t addr, size_t len);
static int lmi_flash_write(target *t, uint32_t dest,
const uint8_t *src, size_t len);
static int lmi_flash_erase(struct target_flash *f, uint32_t addr, size_t len);
static int lmi_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len);
static const char lmi_driver_str[] = "TI Stellaris/Tiva";
static const char lmi_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>"
" <memory type=\"flash\" start=\"0\" length=\"0x20000\">"
" <property name=\"blocksize\">0x400</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x10000\"/>"
"</memory-map>";
static const char tm4c123gh6pm_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>"
" <memory type=\"flash\" start=\"0\" length=\"0x40000\">"
" <property name=\"blocksize\">0x400</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x8000\"/>"
"</memory-map>";
static const uint16_t lmi_flash_write_stub[] = {
#include "../flashstub/lmi.stub"
};
static void lmi_add_flash(target *t, size_t length)
{
struct target_flash *f = calloc(1, sizeof(*f));
f->start = 0;
f->length = length;
f->blocksize = 0x400;
f->erase = lmi_flash_erase;
f->write = lmi_flash_write;
f->align = 4;
f->erased = 0xff;
target_add_flash(t, f);
}
bool lmi_probe(target *t)
{
uint32_t did1 = target_mem_read32(t, LMI_SCB_DID1);
switch (did1 >> 16) {
case 0x1049: /* LM3S3748 */
t->driver = lmi_driver_str;
t->xml_mem_map = lmi_xml_memory_map;
t->flash_erase = lmi_flash_erase;
t->flash_write = lmi_flash_write;
target_add_ram(t, 0x20000000, 0x8000);
lmi_add_flash(t, 0x40000);
return true;
case 0x10A1: /* TM4C123GH6PM */
t->driver = lmi_driver_str;
t->xml_mem_map = tm4c123gh6pm_xml_memory_map;
t->flash_erase = lmi_flash_erase;
t->flash_write = lmi_flash_write;
target_add_ram(t, 0x20000000, 0x10000);
lmi_add_flash(t, 0x80000);
return true;
}
return false;
}
int lmi_flash_erase(target *t, uint32_t addr, size_t len)
int lmi_flash_erase(struct target_flash *f, uint32_t addr, size_t len)
{
addr &= ~(BLOCK_SIZE - 1);
len &= ~(BLOCK_SIZE - 1);
target *t = f->t;
while(len) {
target_mem_write32(t, LMI_FLASH_FMA, addr);
target_mem_write32(t, LMI_FLASH_FMC,
@ -118,16 +104,14 @@ int lmi_flash_erase(target *t, uint32_t addr, size_t len)
return 0;
}
int lmi_flash_write(target *t, uint32_t dest, const uint8_t *src, size_t len)
int lmi_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len)
{
uint32_t data[(len>>2)+2];
data[0] = dest;
data[1] = len >> 2;
memcpy(&data[2], src, len);
target *t = f->t;
target_mem_write(t, SRAM_BASE, lmi_flash_write_stub,
sizeof(lmi_flash_write_stub));
target_mem_write(t, STUB_BUFFER_BASE, data, len + 8);
return cortexm_run_stub(t, SRAM_BASE, 0, 0, 0, 0);
target_mem_write(t, STUB_BUFFER_BASE, src, len);
return cortexm_run_stub(t, SRAM_BASE, dest, STUB_BUFFER_BASE, len, 0);
}

291
src/lpc11xx.c

@ -1,6 +1,9 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Mike Smith <drziplok@me.com>
* Copyright (C) 2015 Gareth McMullin <gareth@blacksphere.co.nz>
*
* 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
@ -14,93 +17,36 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "general.h"
#include "adiv5.h"
#include "target.h"
#include "cortexm.h"
#include "lpc_common.h"
#define IAP_PGM_CHUNKSIZE 256 /* should fit in RAM on any device */
struct flash_param {
uint16_t opcodes[2]; /* two opcodes to return to after calling the ROM */
uint32_t command[5]; /* command operands */
uint32_t result[4]; /* result data */
};
struct flash_program {
struct flash_param p;
uint8_t data[IAP_PGM_CHUNKSIZE];
};
#define IAP_PGM_CHUNKSIZE 512 /* should fit in RAM on any device */
static struct flash_program flash_pgm;
#define MSP 17 /* Main stack pointer register number */
#define MIN_RAM_SIZE_FOR_LPC8xx 1024
#define MIN_RAM_SIZE_FOR_LPC1xxx 2048
#define MIN_RAM_SIZE 1024
#define RAM_USAGE_FOR_IAP_ROUTINES 32 /* IAP routines use 32 bytes at top of ram */
#define IAP_ENTRYPOINT 0x1fff1ff1
#define IAP_RAM_BASE 0x10000000
#define IAP_CMD_PREPARE 50
#define IAP_CMD_PROGRAM 51
#define IAP_CMD_ERASE 52
#define IAP_CMD_BLANKCHECK 53
#define IAP_STATUS_CMD_SUCCESS 0
#define IAP_STATUS_INVALID_COMMAND 1
#define IAP_STATUS_SRC_ADDR_ERROR 2
#define IAP_STATUS_DST_ADDR_ERROR 3
#define IAP_STATUS_SRC_ADDR_NOT_MAPPED 4
#define IAP_STATUS_DST_ADDR_NOT_MAPPED 5
#define IAP_STATUS_COUNT_ERROR 6
#define IAP_STATUS_INVALID_SECTOR 7
#define IAP_STATUS_SECTOR_NOT_BLANK 8
#define IAP_STATUS_SECTOR_NOT_PREPARED 9
#define IAP_STATUS_COMPARE_ERROR 10
#define IAP_STATUS_BUSY 11
#define LPC11XX_DEVICE_ID 0x400483F4
#define LPC8XX_DEVICE_ID 0x400483F8
static const char lpc8xx_driver[] = "lpc8xx";
static const char lpc11xx_driver[] = "lpc11xx";
static void lpc11x_iap_call(target *t, struct flash_param *param, unsigned param_len);
static int lpc11xx_flash_prepare(target *t, uint32_t addr, int len);
static int lpc11xx_flash_erase(target *t, uint32_t addr, size_t len);
static int lpc11xx_flash_write(target *t, uint32_t dest, const uint8_t *src,
size_t len);
static int lpc11xx_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len);
/*
* Note that this memory map is actually for the largest of the lpc11xx devices;
* There seems to be no good way to decode the part number to determine the RAM
* and flash sizes.
*/
static const char lpc11xx_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>"
" <memory type=\"flash\" start=\"0x00000000\" length=\"0x20000\">"
" <property name=\"blocksize\">0x1000</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x10000000\" length=\"0x2000\"/>"
"</memory-map>";
/*
* Memory map for the lpc8xx devices, which otherwise look much like the lpc11xx.
*
* We could decode the RAM/flash sizes, but we just encode the largest possible here.
*
* Note that the LPC810 and LPC811 map their flash oddly; see the NXP LPC800 user
* manual (UM10601) for more details.
*/
static const char lpc8xx_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>"
" <memory type=\"flash\" start=\"0x00000000\" length=\"0x4000\">"
" <property name=\"blocksize\">0x400</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x10000000\" length=\"0x1000\"/>"
"</memory-map>";
void lpc11xx_add_flash(target *t, uint32_t addr, size_t len, size_t erasesize)
{
struct lpc_flash *lf = lpc_add_flash(t, addr, len);
lf->f.blocksize = erasesize;
lf->f.buf_size = IAP_PGM_CHUNKSIZE;
lf->f.write_buf = lpc11xx_flash_write;
lf->iap_entry = IAP_ENTRYPOINT;
lf->iap_ram = IAP_RAM_BASE;
lf->iap_msp = IAP_RAM_BASE + MIN_RAM_SIZE - RAM_USAGE_FOR_IAP_ROUTINES;
}
bool
lpc11xx_probe(target *t)
@ -108,10 +54,8 @@ lpc11xx_probe(target *t)
uint32_t idcode;
/* read the device ID register */
idcode = target_mem_read32(t, 0x400483F4);
idcode = target_mem_read32(t, LPC11XX_DEVICE_ID);
switch (idcode) {
case 0x041E502B:
case 0x2516D02B:
case 0x0416502B:
@ -143,179 +87,40 @@ lpc11xx_probe(target *t)
case 0x2972402B: /* lpc11u23/301 */
case 0x2988402B: /* lpc11u24x/301 */
case 0x2980002B: /* lpc11u24x/401 */
t->driver = lpc11xx_driver;
t->xml_mem_map = lpc11xx_xml_memory_map;
t->flash_erase = lpc11xx_flash_erase;
t->flash_write = lpc11xx_flash_write;
t->driver = "LPC11xx";
target_add_ram(t, 0x10000000, 0x2000);
lpc11xx_add_flash(t, 0x00000000, 0x20000, 0x1000);
return true;
}
case 0x1812202b: /* LPC812M101FDH20 */
t->driver = lpc8xx_driver;
t->xml_mem_map = lpc8xx_xml_memory_map;
t->flash_erase = lpc11xx_flash_erase;
t->flash_write = lpc11xx_flash_write;
idcode = target_mem_read32(t, LPC8XX_DEVICE_ID);
switch (idcode) {
case 0x00008100: /* LPC810M021FN8 */
case 0x00008110: /* LPC811M001JDH16 */
case 0x00008120: /* LPC812M101JDH16 */
case 0x00008121: /* LPC812M101JD20 */
case 0x00008122: /* LPC812M101JDH20 / LPC812M101JTB16 */
t->driver = "LPC8xx";
target_add_ram(t, 0x10000000, 0x1000);
lpc11xx_add_flash(t, 0x00000000, 0x4000, 0x400);
return true;
}
return false;
}
static void
lpc11x_iap_call(target *t, struct flash_param *param, unsigned param_len)
{
uint32_t regs[t->regs_size / sizeof(uint32_t)];
/* fill out the remainder of the parameters and copy the structure to RAM */
param->opcodes[0] = 0xbe00;
param->opcodes[1] = 0x0000;
target_mem_write(t, IAP_RAM_BASE, param, param_len);
/* set up for the call to the IAP ROM */
target_regs_read(t, regs);
regs[0] = IAP_RAM_BASE + offsetof(struct flash_param, command);
regs[1] = IAP_RAM_BASE + offsetof(struct flash_param, result);
// stack pointer - top of the smallest ram less 32 for IAP usage
if (t->driver == lpc8xx_driver)
regs[MSP] = IAP_RAM_BASE + MIN_RAM_SIZE_FOR_LPC8xx - RAM_USAGE_FOR_IAP_ROUTINES;
else
regs[MSP] = IAP_RAM_BASE + MIN_RAM_SIZE_FOR_LPC1xxx - RAM_USAGE_FOR_IAP_ROUTINES;
regs[14] = IAP_RAM_BASE | 1;
regs[15] = IAP_ENTRYPOINT;
target_regs_write(t, regs);
/* start the target and wait for it to halt again */
target_halt_resume(t, 0);
while (!target_halt_wait(t));
/* copy back just the parameters structure */
target_mem_read(t, param, IAP_RAM_BASE, sizeof(struct flash_param));
}
static int flash_page_size(target *t)
{
if (t->driver == lpc8xx_driver)
return 1024;
else
return 4096;
}
static int
lpc11xx_flash_prepare(target *t, uint32_t addr, int len)
static int lpc11xx_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len)
{
/* prepare the sector(s) to be erased */
memset(&flash_pgm.p, 0, sizeof(flash_pgm.p));
flash_pgm.p.command[0] = IAP_CMD_PREPARE;
flash_pgm.p.command[1] = addr / flash_page_size(t);
flash_pgm.p.command[2] = (addr + len - 1) / flash_page_size(t);
lpc11x_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
return -1;
if (dest == 0) {
/* Fill in the magic vector to allow booting the flash */
uint32_t *w = (uint32_t *)src;
uint32_t sum = 0;
for (unsigned i = 0; i < 7; i++)
sum += w[i];
w[7] = ~sum + 1;
}
return 0;
return lpc_flash_write(f, dest, src, len);
}
static int
lpc11xx_flash_erase(target *t, uint32_t addr, size_t len)
{
if (addr % flash_page_size(t))
return -1;
/* prepare... */
if (lpc11xx_flash_prepare(t, addr, len))
return -1;
/* and now erase them */
flash_pgm.p.command[0] = IAP_CMD_ERASE;
flash_pgm.p.command[1] = addr / flash_page_size(t);
flash_pgm.p.command[2] = (addr + len - 1) / flash_page_size(t);
flash_pgm.p.command[3] = 12000; /* XXX safe to assume this? */
lpc11x_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
return -1;
}
flash_pgm.p.command[0] = IAP_CMD_BLANKCHECK;
lpc11x_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
return -1;
}
return 0;
}
static int
lpc11xx_flash_write(target *t, uint32_t dest, const uint8_t *src, size_t len)
{
unsigned first_chunk = dest / IAP_PGM_CHUNKSIZE;
unsigned last_chunk = (dest + len - 1) / IAP_PGM_CHUNKSIZE;
unsigned chunk_offset = dest % IAP_PGM_CHUNKSIZE;
unsigned chunk;
for (chunk = first_chunk; chunk <= last_chunk; chunk++) {
DEBUG("chunk %u len %zu\n", chunk, len);
/* first and last chunk may require special handling */
if ((chunk == first_chunk) || (chunk == last_chunk)) {
/* fill with all ff to avoid sector rewrite corrupting other writes */
memset(flash_pgm.data, 0xff, sizeof(flash_pgm.data));
/* copy as much as fits */
size_t copylen = IAP_PGM_CHUNKSIZE - chunk_offset;
if (copylen > len)
copylen = len;
memcpy(&flash_pgm.data[chunk_offset], src, copylen);
/* if we are programming the vectors, calculate the magic number */
if ((chunk == 0) && (chunk_offset == 0)) {
if (copylen < 32) {
/* we have to be programming at least the first 8 vectors... */
return -1;
}
uint32_t *w = (uint32_t *)(&flash_pgm.data[0]);
uint32_t sum = 0;
for (unsigned i = 0; i < 7; i++)
sum += w[i];
w[7] = ~sum + 1;
}
/* update to suit */
len -= copylen;
src += copylen;
chunk_offset = 0;
} else {
/* interior chunk, must be aligned and full-sized */
memcpy(flash_pgm.data, src, IAP_PGM_CHUNKSIZE);
len -= IAP_PGM_CHUNKSIZE;
src += IAP_PGM_CHUNKSIZE;
}
/* prepare... */
if (lpc11xx_flash_prepare(t, chunk * IAP_PGM_CHUNKSIZE, IAP_PGM_CHUNKSIZE))
return -1;
/* set the destination address and program */
flash_pgm.p.command[0] = IAP_CMD_PROGRAM;
flash_pgm.p.command[1] = chunk * IAP_PGM_CHUNKSIZE;
flash_pgm.p.command[2] = IAP_RAM_BASE + offsetof(struct flash_program, data);
flash_pgm.p.command[3] = IAP_PGM_CHUNKSIZE;
/* assuming we are running off IRC - safe lower bound */
flash_pgm.p.command[4] = 12000; /* XXX safe to presume this? */
lpc11x_iap_call(t, &flash_pgm.p, sizeof(flash_pgm));
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
return -1;
}
}
return 0;
}

417
src/lpc43xx.c

@ -1,7 +1,8 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2012 Gareth McMullin <gareth@blacksphere.co.nz>
* Copyright (C) 2014 Allen Ibara <aibara>
* Copyright (C) 2015 Gareth McMullin <gareth@blacksphere.co.nz>
*
* 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
@ -19,17 +20,13 @@
#include "general.h"
#include "command.h"
#include "adiv5.h"
#include "target.h"
#include "gdb_packet.h"
#include "cortexm.h"
#include "lpc_common.h"
#define LPC43XX_CHIPID 0x40043200
#define ARM_CPUID 0xE000ED00
#define ARM_THUMB_BREAKPOINT 0xBE00
#define R_MSP 17 // Main stack pointer register number
#define R_PC 15 // Program counter register number
#define R_LR 14 // Link register number
#define IAP_ENTRYPOINT_LOCATION 0x10400100
@ -47,89 +44,14 @@
#define IAP_PGM_CHUNKSIZE 4096
#define IAP_CMD_INIT 49
#define IAP_CMD_PREPARE 50
#define IAP_CMD_PROGRAM 51
#define IAP_CMD_ERASE 52
#define IAP_CMD_BLANKCHECK 53
#define IAP_CMD_SET_ACTIVE_BANK 60
#define IAP_STATUS_CMD_SUCCESS 0
#define IAP_STATUS_INVALID_COMMAND 1
#define IAP_STATUS_SRC_ADDR_ERROR 2
#define IAP_STATUS_DST_ADDR_ERROR 3
#define IAP_STATUS_SRC_ADDR_NOT_MAPPED 4
#define IAP_STATUS_DST_ADDR_NOT_MAPPED 5
#define IAP_STATUS_COUNT_ERROR 6
#define IAP_STATUS_INVALID_SECTOR 7
#define IAP_STATUS_SECTOR_NOT_BLANK 8
#define IAP_STATUS_SECTOR_NOT_PREPARED 9
#define IAP_STATUS_COMPARE_ERROR 10
#define IAP_STATUS_BUSY 11
#define FLASH_BANK_A_BASE 0x1A000000
#define FLASH_BANK_A_SIZE 0x80000
#define FLASH_BANK_B_BASE 0x1B000000
#define FLASH_BANK_B_SIZE 0x80000
#define FLASH_NUM_BANK 2
#define FLASH_NUM_SECTOR 15
#define FLASH_LARGE_SECTOR_OFFSET 0x00010000
/* CPU Frequency */
#define CPU_CLK_KHZ 12000
struct flash_param {
uint16_t opcode; /* opcode to return to after calling the ROM */
uint16_t pad0;
uint32_t command; /* IAP command */
union {
uint32_t words[5]; /* command parameters */
struct {
uint32_t start_sector;
uint32_t end_sector;
uint32_t flash_bank;
} prepare;
struct {
uint32_t start_sector;
uint32_t end_sector;
uint32_t cpu_clk_khz;
uint32_t flash_bank;
} erase;
struct {
uint32_t dest;
uint32_t source;
uint32_t byte_count;
uint32_t cpu_clk_khz;
} program;
struct {
uint32_t start_sector;
uint32_t end_sector;
uint32_t flash_bank;
} blank_check;
struct {
uint32_t flash_bank;
uint32_t cpu_clk_khz;
} make_active;
} params;
uint32_t result[5]; /* result data */
} __attribute__((aligned(4)));
struct flash_program {
struct flash_param p;
uint8_t data[IAP_PGM_CHUNKSIZE];
};
static bool lpc43xx_cmd_erase(target *t, int argc, const char *argv[]);
static bool lpc43xx_cmd_reset(target *t, int argc, const char *argv[]);
static bool lpc43xx_cmd_mkboot(target *t, int argc, const char *argv[]);
static int lpc43xx_flash_init(target *t);
static void lpc43xx_iap_call(target *t, struct flash_param *param,
unsigned param_len);
static int lpc43xx_flash_prepare(target *t,
uint32_t addr, int len);
static int lpc43xx_flash_erase(target *t, uint32_t addr, size_t len);
static int lpc43xx_flash_write(target *t,
uint32_t dest, const uint8_t *src, size_t len);
static int lpc43xx_flash_erase(struct target_flash *f, uint32_t addr, size_t len);
static void lpc43xx_set_internal_clock(target *t);
static void lpc43xx_wdt_set_period(target *t);
static void lpc43xx_wdt_pet(target *t);
@ -141,50 +63,54 @@ const struct command_s lpc43xx_cmd_list[] = {
{NULL, NULL, NULL}
};
/* blocksize is the erasure block size */
static const char lpc4337_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>"
" <memory type=\"ram\" start=\"0x0\" length=\"0x1A000000\"/>"
" <memory type=\"flash\" start=\"0x1A000000\" length=\"0x10000\">"
" <property name=\"blocksize\">0x2000</property>"
" </memory>"
" <memory type=\"flash\" start=\"0x1A010000\" length=\"0x70000\">"
" <property name=\"blocksize\">0x10000</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x1A080000\" length=\"0x00F80000\"/>"
" <memory type=\"flash\" start=\"0x1B000000\" length=\"0x10000\">"
" <property name=\"blocksize\">0x2000</property>"
" </memory>"
" <memory type=\"flash\" start=\"0x1B010000\" length=\"0x70000\">"
" <property name=\"blocksize\">0x10000</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x1B080000\" length=\"0xE4F80000\"/>"
"</memory-map>";
void lpc43xx_add_flash(target *t, uint32_t iap_entry,
uint8_t bank, uint8_t base_sector,
uint32_t addr, size_t len, size_t erasesize)
{
struct lpc_flash *lf = lpc_add_flash(t, addr, len);
lf->f.erase = lpc43xx_flash_erase;
lf->f.blocksize = erasesize;
lf->f.buf_size = IAP_PGM_CHUNKSIZE;
lf->bank = bank;
lf->base_sector = base_sector;
lf->iap_entry = iap_entry;
lf->iap_ram = IAP_RAM_BASE;
lf->iap_msp = IAP_RAM_BASE + IAP_RAM_SIZE;
lf->wdt_kick = lpc43xx_wdt_pet;
}
bool lpc43xx_probe(target *t)
{
uint32_t chipid, cpuid;
uint32_t iap_entry;
chipid = target_mem_read32(t, LPC43XX_CHIPID);
cpuid = target_mem_read32(t, ARM_CPUID);
switch(chipid) {
case 0x4906002B: /* Parts with on-chip flash */
case 0x7906002B: /* LM43S?? - Undocumented? */
switch (cpuid & 0xFF00FFF0) {
case 0x4100C240:
t->driver = "LPC43xx Cortex-M4";
if (cpuid == 0x410FC241)
{
/* LPC4337 */
t->xml_mem_map = lpc4337_xml_memory_map;
t->flash_erase = lpc43xx_flash_erase;
t->flash_write = lpc43xx_flash_write;
iap_entry = target_mem_read32(t,
IAP_ENTRYPOINT_LOCATION);
target_add_ram(t, 0, 0x1A000000);
lpc43xx_add_flash(t, iap_entry, 0, 0,
0x1A000000, 0x10000, 0x2000);
lpc43xx_add_flash(t, iap_entry, 0, 8,
0x1A010000, 0x70000, 0x10000);
target_add_ram(t, 0x1A080000, 0xF80000);
lpc43xx_add_flash(t, iap_entry, 1, 0,
0x1B000000, 0x10000, 0x2000);
lpc43xx_add_flash(t, iap_entry, 1, 8,
0x1B010000, 0x70000, 0x10000);
target_add_commands(t, lpc43xx_cmd_list, "LPC43xx");
target_add_ram(t, 0x1B080000, 0xE4F80000UL);
t->target_options |= CORTEXM_TOPT_INHIBIT_SRST;
}
break;
case 0x4100C200:
@ -234,34 +160,18 @@ static bool lpc43xx_cmd_erase(target *t, int argc, const char *argv[])
(void)argc;
(void)argv;
uint32_t bank = 0;
struct flash_program flash_pgm;
lpc43xx_flash_init(t);
for (bank = 0; bank < FLASH_NUM_BANK; bank++)
for (int bank = 0; bank < FLASH_NUM_BANK; bank++)
{
flash_pgm.p.command = IAP_CMD_PREPARE;
flash_pgm.p.params.prepare.start_sector = 0;
flash_pgm.p.params.prepare.end_sector = FLASH_NUM_SECTOR-1;
flash_pgm.p.params.prepare.flash_bank = bank;
flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
struct lpc_flash *f = (struct lpc_flash *)t->flash;
if (lpc_iap_call(f, IAP_CMD_PREPARE,
0, FLASH_NUM_SECTOR-1, bank))
return false;
}
flash_pgm.p.command = IAP_CMD_ERASE;
flash_pgm.p.params.erase.start_sector = 0;
flash_pgm.p.params.prepare.end_sector = FLASH_NUM_SECTOR-1;
flash_pgm.p.params.erase.cpu_clk_khz = CPU_CLK_KHZ;
flash_pgm.p.params.erase.flash_bank = bank;
flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS)
{
if (lpc_iap_call(f, IAP_CMD_ERASE,
0, FLASH_NUM_SECTOR-1, CPU_CLK_KHZ, bank))
return false;
}
}
gdb_outf("Erase OK.\n");
@ -277,224 +187,26 @@ static int lpc43xx_flash_init(target *t)
/* Force internal clock */
lpc43xx_set_internal_clock(t);
struct flash_program flash_pgm;
/* Initialize flash IAP */
flash_pgm.p.command = IAP_CMD_INIT;
flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS)
return -1;
return 0;
}
/**
* @brief find a sector number given linear offset
*/
static int32_t flash_bank(uint32_t addr)
{
if ((addr >= FLASH_BANK_A_BASE) &&
(addr < (FLASH_BANK_A_BASE + FLASH_BANK_A_SIZE)))
return 0;
if ((addr >= FLASH_BANK_B_BASE) &&
(addr < (FLASH_BANK_B_BASE + FLASH_BANK_B_SIZE)))
return 1;
return -1;
}
/**
* @brief find a sector number given linear offset
*/
static int32_t sector_number(uint32_t addr)
{
int32_t bank = flash_bank(addr);
switch (bank) {
case 0:
addr = addr - FLASH_BANK_A_BASE;
break;
case 1:
addr = addr - FLASH_BANK_B_BASE;
break;
default:
return -1;
}
/* from 47.5 "Sector numbers" (page 1218) UM10503.pdf (Rev 1.6) */
if (addr < FLASH_LARGE_SECTOR_OFFSET) {
return addr >> 13;
} else {
return 8 + ((addr - FLASH_LARGE_SECTOR_OFFSET) >> 16);
}
}
static void lpc43xx_iap_call(target *t, struct flash_param *param, unsigned param_len)
{
uint32_t regs[t->regs_size / sizeof(uint32_t)];
uint32_t iap_entry;
/* Pet WDT before each IAP call, if it is on */
lpc43xx_wdt_pet(t);
target_mem_read(t, &iap_entry, IAP_ENTRYPOINT_LOCATION, sizeof(iap_entry));
/* fill out the remainder of the parameters and copy the structure to RAM */
param->opcode = ARM_THUMB_BREAKPOINT; /* breakpoint */
param->pad0 = 0x0000; /* pad */
target_mem_write(t, IAP_RAM_BASE, param, param_len);
/* set up for the call to the IAP ROM */
target_regs_read(t, regs);
regs[0] = IAP_RAM_BASE + offsetof(struct flash_param, command);
regs[1] = IAP_RAM_BASE + offsetof(struct flash_param, result);
regs[R_MSP] = IAP_RAM_BASE + IAP_RAM_SIZE;
regs[R_LR] = IAP_RAM_BASE | 1;
regs[R_PC] = iap_entry;
target_regs_write(t, regs);
/* start the target and wait for it to halt again */
target_halt_resume(t, 0);
while (!target_halt_wait(t));
/* copy back just the parameters structure */
target_mem_read(t, param, IAP_RAM_BASE, sizeof(struct flash_param));
}
static int lpc43xx_flash_prepare(target *t, uint32_t addr, int len)
{
struct flash_program flash_pgm;
/* prepare the sector(s) to be erased */
flash_pgm.p.command = IAP_CMD_PREPARE;
flash_pgm.p.params.prepare.start_sector = sector_number(addr);
flash_pgm.p.params.prepare.end_sector = sector_number(addr+len);
flash_pgm.p.params.prepare.flash_bank = flash_bank(addr);
flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
struct lpc_flash *f = (struct lpc_flash *)t->flash;
if (lpc_iap_call(f, IAP_CMD_INIT))
return -1;
}
return 0;
}
static int lpc43xx_flash_erase(target *t, uint32_t addr, size_t len)
static int lpc43xx_flash_erase(struct target_flash *f, uint32_t addr, size_t len)
{
struct flash_program flash_pgm;
/* min block size */
if (addr % 8192)
return -1;
/* init */
if (lpc43xx_flash_init(t))
return -1;
/* prepare... */
if (lpc43xx_flash_prepare(t, addr, len))
return -1;
/* and now erase them */
flash_pgm.p.command = IAP_CMD_ERASE;
flash_pgm.p.params.erase.start_sector = sector_number(addr);
flash_pgm.p.params.erase.end_sector = sector_number(addr+len);
flash_pgm.p.params.erase.cpu_clk_khz = CPU_CLK_KHZ;
flash_pgm.p.params.erase.flash_bank = flash_bank(addr);
flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
if (lpc43xx_flash_init(f->t))
return -1;
}
/* check erase ok */
flash_pgm.p.command = IAP_CMD_BLANKCHECK;
flash_pgm.p.params.blank_check.start_sector = sector_number(addr);
flash_pgm.p.params.blank_check.end_sector = sector_number(addr+len);
flash_pgm.p.params.blank_check.flash_bank = flash_bank(addr);
flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm.p));
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
return -1;
}
return 0;
return lpc_flash_erase(f, addr, len);
}
static void lpc43xx_set_internal_clock(target *t)
{
const uint32_t val2 = (1 << 11) | (1 << 24);
target_mem_write(t, 0x40050000 + 0x06C, &val2, sizeof(val2));
}
static int lpc43xx_flash_write(target *t,
uint32_t dest, const uint8_t *src, size_t len)
{
unsigned first_chunk = dest / IAP_PGM_CHUNKSIZE;
unsigned last_chunk = (dest + len - 1) / IAP_PGM_CHUNKSIZE;
unsigned chunk_offset;
unsigned chunk;
struct flash_program flash_pgm;
for (chunk = first_chunk; chunk <= last_chunk; chunk++) {
if (chunk == first_chunk) {
chunk_offset = dest % IAP_PGM_CHUNKSIZE;
} else {
chunk_offset = 0;
}
/* first and last chunk may require special handling */
if ((chunk == first_chunk) || (chunk == last_chunk)) {
/* fill with all ff to avoid sector rewrite corrupting other writes */
memset(flash_pgm.data, 0xff, sizeof(flash_pgm.data));
/* copy as much as fits */
size_t copylen = IAP_PGM_CHUNKSIZE - chunk_offset;
if (copylen > len)
copylen = len;
memcpy(flash_pgm.data + chunk_offset, src, copylen);
/* update to suit */
len -= copylen;
src += copylen;
} else {
/* interior chunk, must be aligned and full-sized */
memcpy(flash_pgm.data, src, IAP_PGM_CHUNKSIZE);
len -= IAP_PGM_CHUNKSIZE;
src += IAP_PGM_CHUNKSIZE;
}
/* prepare... */
if (lpc43xx_flash_prepare(t, chunk * IAP_PGM_CHUNKSIZE, IAP_PGM_CHUNKSIZE)) {
return -1;
}
/* copy buffer into target memory */
target_mem_write(t,
IAP_RAM_BASE + offsetof(struct flash_program, data),
flash_pgm.data, sizeof(flash_pgm.data));
/* set the destination address and program */
flash_pgm.p.command = IAP_CMD_PROGRAM;
flash_pgm.p.params.program.dest = chunk * IAP_PGM_CHUNKSIZE;
flash_pgm.p.params.program.source = IAP_RAM_BASE + offsetof(struct flash_program, data);
flash_pgm.p.params.program.byte_count = IAP_PGM_CHUNKSIZE;
flash_pgm.p.params.program.cpu_clk_khz = CPU_CLK_KHZ;
flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm));
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
return -1;
}
}
return 0;
target_mem_write32(t, 0x40050000 + 0x06C, val2);
}
/*
@ -520,15 +232,10 @@ static bool lpc43xx_cmd_mkboot(target *t, int argc, const char *argv[])
}
lpc43xx_flash_init(t);
struct flash_program flash_pgm;
/* special command to compute/write magic vector for signature */
flash_pgm.p.command = IAP_CMD_SET_ACTIVE_BANK;
flash_pgm.p.params.make_active.flash_bank = bank;
flash_pgm.p.params.make_active.cpu_clk_khz = CPU_CLK_KHZ;
flash_pgm.p.result[0] = IAP_STATUS_CMD_SUCCESS;
lpc43xx_iap_call(t, &flash_pgm.p, sizeof(flash_pgm));
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
struct lpc_flash *f = (struct lpc_flash *)t->flash;
if (lpc_iap_call(f, IAP_CMD_SET_ACTIVE_BANK, bank, CPU_CLK_KHZ)) {
gdb_outf("Set bootable failed.\n");
return false;
}
@ -539,33 +246,23 @@ static bool lpc43xx_cmd_mkboot(target *t, int argc, const char *argv[])
static void lpc43xx_wdt_set_period(target *t)
{
uint32_t wdt_mode = 0;
/* Check if WDT is on */
target_mem_read(t, &wdt_mode, LPC43XX_WDT_MODE, sizeof(wdt_mode));
uint32_t wdt_mode = target_mem_read32(t, LPC43XX_WDT_MODE);
/* If WDT on, we can't disable it, but we may be able to set a long period */
if (wdt_mode && !(wdt_mode & LPC43XX_WDT_PROTECT))
{
const uint32_t wdt_period = LPC43XX_WDT_PERIOD_MAX;
target_mem_write(t, LPC43XX_WDT_CNT, &wdt_period, sizeof(wdt_period));
}
target_mem_write32(t, LPC43XX_WDT_CNT, LPC43XX_WDT_PERIOD_MAX);
}
static void lpc43xx_wdt_pet(target *t)
{
uint32_t wdt_mode = 0;
/* Check if WDT is on */
target_mem_read(t, &wdt_mode, LPC43XX_WDT_MODE, sizeof(wdt_mode));
uint32_t wdt_mode = target_mem_read32(t, LPC43XX_WDT_MODE);
/* If WDT on, pet */
if (wdt_mode)
{
const uint32_t feed1 = 0xAA;;
const uint32_t feed2 = 0x55;;
target_mem_write(t, LPC43XX_WDT_FEED, &feed1, sizeof(feed1));
target_mem_write(t, LPC43XX_WDT_FEED, &feed2, sizeof(feed2));
if (wdt_mode) {
target_mem_write32(t, LPC43XX_WDT_FEED, 0xAA);
target_mem_write32(t, LPC43XX_WDT_FEED, 0xFF);
}
}

135
src/lpc_common.c

@ -0,0 +1,135 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2015 Gareth McMullin <gareth@blacksphere.co.nz>
*
* 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/>.
*/
#include "general.h"
#include "target.h"
#include "cortexm.h"
#include "lpc_common.h"
#include <stdarg.h>
struct flash_param {
uint16_t opcode;
uint16_t pad0;
uint32_t command;
uint32_t words[4];
uint32_t result;
} __attribute__((aligned(4)));
struct lpc_flash *lpc_add_flash(target *t, uint32_t addr, size_t length)
{
struct lpc_flash *lf = calloc(1, sizeof(*lf));
struct target_flash *f = &lf->f;
f->start = addr;
f->length = length;
f->erase = lpc_flash_erase;
f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered;
f->write_buf = lpc_flash_write;
f->erased = 0xff;
target_add_flash(t, f);
return lf;
}
enum iap_status lpc_iap_call(struct lpc_flash *f, enum iap_cmd cmd, ...)
{
target *t = f->f.t;
struct flash_param param = {
.opcode = ARM_THUMB_BREAKPOINT,
.command = cmd,
};
/* Pet WDT before each IAP call, if it is on */
if (f->wdt_kick)
f->wdt_kick(t);
/* fill out the remainder of the parameters */
va_list ap;
va_start(ap, cmd);
for (int i = 0; i < 4; i++)
param.words[i] = va_arg(ap, uint32_t);
va_end(ap);
/* copy the structure to RAM */
target_mem_write(t, f->iap_ram, &param, sizeof(param));
/* set up for the call to the IAP ROM */
uint32_t regs[t->regs_size / sizeof(uint32_t)];
target_regs_read(t, regs);
regs[0] = f->iap_ram + offsetof(struct flash_param, command);
regs[1] = f->iap_ram + offsetof(struct flash_param, result);
regs[REG_MSP] = f->iap_msp;
regs[REG_LR] = f->iap_ram | 1;
regs[REG_PC] = f->iap_entry;
target_regs_write(t, regs);
/* start the target and wait for it to halt again */
target_halt_resume(t, false);
while (!target_halt_wait(t));
/* copy back just the parameters structure */
target_mem_read(t, &param, f->iap_ram, sizeof(param));
return param.result;
}
static uint8_t lpc_sector_for_addr(struct lpc_flash *f, uint32_t addr)
{
return f->base_sector + (addr - f->f.start) / f->f.blocksize;
}
int lpc_flash_erase(struct target_flash *tf, uint32_t addr, size_t len)
{
struct lpc_flash *f = (struct lpc_flash *)tf;
uint32_t start = lpc_sector_for_addr(f, addr);
uint32_t end = lpc_sector_for_addr(f, addr + len - 1);
if (lpc_iap_call(f, IAP_CMD_PREPARE, start, end, f->bank))
return -1;
/* and now erase them */
if (lpc_iap_call(f, IAP_CMD_ERASE, start, end, CPU_CLK_KHZ, f->bank))
return -2;
/* check erase ok */
if (lpc_iap_call(f, IAP_CMD_BLANKCHECK, start, end, f->bank))
return -3;
return 0;
}
int lpc_flash_write(struct target_flash *tf,
uint32_t dest, const void *src, size_t len)
{
struct lpc_flash *f = (struct lpc_flash *)tf;
/* prepare... */
uint32_t sector = lpc_sector_for_addr(f, dest);
if (lpc_iap_call(f, IAP_CMD_PREPARE, sector, sector, f->bank))
return -1;
/* Write payload to target ram */
uint32_t bufaddr = ALIGN(f->iap_ram + sizeof(struct flash_param), 4);
target_mem_write(f->f.t, bufaddr, src, len);
/* set the destination address and program */
if (lpc_iap_call(f, IAP_CMD_PROGRAM, dest, bufaddr, len, CPU_CLK_KHZ))
return -2;
return 0;
}

69
src/nrf51.c

@ -28,9 +28,9 @@
#include "gdb_packet.h"
#include "cortexm.h"
static int nrf51_flash_erase(target *t, uint32_t addr, size_t len);
static int nrf51_flash_write(target *t, uint32_t dest,
const uint8_t *src, size_t len);
static int nrf51_flash_erase(struct target_flash *f, uint32_t addr, size_t len);
static int nrf51_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len);
static bool nrf51_cmd_erase_all(target *t);
static bool nrf51_cmd_read_hwid(target *t);
@ -54,20 +54,6 @@ const struct command_s nrf51_read_cmd_list[] = {
{NULL, NULL, NULL}
};
static const char nrf51_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>"
" <memory type=\"flash\" start=\"0x0\" length=\"0x40000\">"
" <property name=\"blocksize\">0x400</property>"
" </memory>"
" <memory type=\"flash\" start=\"0x10001000\" length=\"0x100\">"
" <property name=\"blocksize\">0x400</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x4000\"/>"
"</memory-map>";
/* Non-Volatile Memory Controller (NVMC) Registers */
#define NRF51_NVMC 0x4001E000
#define NRF51_NVMC_READY (NRF51_NVMC + 0x400)
@ -103,6 +89,20 @@ static const uint16_t nrf51_flash_write_stub[] = {
#include "../flashstub/nrf51.stub"
};
static void nrf51_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 = nrf51_flash_erase;
f->write = nrf51_flash_write;
f->align = 4;
f->erased = 0xff;
target_add_flash(t, f);
}
bool nrf51_probe(target *t)
{
t->idcode = target_mem_read32(t, NRF51_FICR_CONFIGID) & 0xFFFF;
@ -120,9 +120,9 @@ bool nrf51_probe(target *t)
case 0x0026:
case 0x004C:
t->driver = "Nordic nRF51";
t->xml_mem_map = nrf51_xml_memory_map;
t->flash_erase = nrf51_flash_erase;
t->flash_write = nrf51_flash_write;
target_add_ram(t, 0x20000000, 0x4000);
nrf51_add_flash(t, 0x00000000, 0x40000, NRF51_PAGE_SIZE);
nrf51_add_flash(t, NRF51_UICR, 0x100, 0x100);
target_add_commands(t, nrf51_cmd_list, "nRF51");
return true;
}
@ -130,11 +130,9 @@ bool nrf51_probe(target *t)
return false;
}
static int nrf51_flash_erase(target *t, uint32_t addr, size_t len)
static int nrf51_flash_erase(struct target_flash *f, uint32_t addr, size_t len)
{
addr &= ~(NRF51_PAGE_SIZE - 1);
len &= ~(NRF51_PAGE_SIZE - 1);
target *t = f->t;
/* Enable erase */
target_mem_write32(t, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_EEN);
@ -158,8 +156,8 @@ static int nrf51_flash_erase(target *t, uint32_t addr, size_t len)
if(target_check_error(t))
return -1;
addr += NRF51_PAGE_SIZE;
len -= NRF51_PAGE_SIZE;
addr += f->blocksize;
len -= f->blocksize;
}
/* Return to read-only */
@ -173,19 +171,18 @@ static int nrf51_flash_erase(target *t, uint32_t addr, size_t len)
return 0;
}
static int nrf51_flash_write(target *t, uint32_t dest,
const uint8_t *src, size_t len)
static int nrf51_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len)
{
uint32_t offset = dest % 4;
uint32_t words = (offset + len + 3) / 4;
uint32_t data[2 + words];
target *t = f->t;
uint32_t data[2 + len/4];
/* FIXME rewrite stub to use register args */
/* Construct data buffer used by stub */
data[0] = dest - offset;
data[1] = words * 4; /* length must always be a multiple of 4 */
data[2] = 0xFFFFFFFF; /* pad partial words with all 1s to avoid */
data[words + 1] = 0xFFFFFFFF; /* damaging overlapping areas */
memcpy((uint8_t *)&data[2] + offset, src, len);
data[0] = dest;
data[1] = len; /* length must always be a multiple of 4 */
memcpy((uint8_t *)&data[2], src, len);
/* Enable write */
target_mem_write32(t, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_WEN);

1
src/platforms/native/platform.h

@ -150,6 +150,7 @@
/* Use newlib provided integer only stdio functions */
#define sscanf siscanf
#define sprintf siprintf
#define snprintf sniprintf
#define vasprintf vasiprintf
#endif

387
src/sam3x.c

@ -1,7 +1,7 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2012 Black Sphere Technologies Ltd.
* Copyright (C) 2015 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
@ -18,8 +18,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements Atmel SAM3X target specific functions for detecting
/* This file implements Atmel SAM3/4 target specific functions for detecting
* the device, providing the XML memory map and Flash memory programming.
*
* Supported devices: SAM3N, SAM3S, SAM3U, SAM3X, and SAM4S
*/
#include "general.h"
@ -28,9 +30,10 @@
#include "command.h"
#include "gdb_packet.h"
static int sam3x_flash_erase(target *t, uint32_t addr, size_t len);
static int sam3x_flash_write(target *t, uint32_t dest,
const uint8_t *src, size_t len);
static int sam4_flash_erase(struct target_flash *f, uint32_t addr, size_t len);
static int sam3_flash_erase(struct target_flash *f, uint32_t addr, size_t len);
static int sam3x_flash_write(struct target_flash *f, uint32_t dest,
const void *src, size_t len);
static bool sam3x_cmd_gpnvm_get(target *t);
static bool sam3x_cmd_gpnvm_set(target *t, int argc, char *argv[]);
@ -41,54 +44,6 @@ const struct command_s sam3x_cmd_list[] = {
{NULL, NULL, NULL}
};
static const char sam3x_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>"
" <memory type=\"flash\" start=\"0x80000\" length=\"0x80000\">"
" <property name=\"blocksize\">0x100</property>"
" </memory>"
" <memory type=\"rom\" start=\"0x100000\" length=\"0x200000\"/>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x200000\"/>"
"</memory-map>";
static const char sam3n_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>"
" <memory type=\"flash\" start=\"0x400000\" length=\"0x400000\">"
" <property name=\"blocksize\">0x100</property>"
" </memory>"
" <memory type=\"rom\" start=\"0x800000\" length=\"0x400000\"/>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x200000\"/>"
"</memory-map>";
static const char sam3u_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>"
" <memory type=\"flash\" start=\"0x80000\" length=\"0x100000\">"
" <property name=\"blocksize\">0x100</property>"
" </memory>"
" <memory type=\"rom\" start=\"0x180000\" length=\"0x200000\"/>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x200000\"/>"
"</memory-map>";
static const char sam4s_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>"
" <memory type=\"flash\" start=\"0x400000\" length=\"0x400000\">"
" <property name=\"blocksize\">0x200</property>"
" </memory>"
" <memory type=\"rom\" start=\"0x800000\" length=\"0x400000\"/>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x400000\"/>"
"</memory-map>";
/* Enhanced Embedded Flash Controller (EEFC) Register Map */
#define SAM3N_EEFC_BASE 0x400E0A00
#define SAM3X_EEFC_BASE(x) (0x400E0A00+((x)*0x400))
@ -122,16 +77,17 @@ static const char sam4s_xml_memory_map[] = "<?xml version=\"1.0\"?>"
#define EEFC_FSR_ERROR (EEFC_FSR_FCMDE | EEFC_FSR_FLOCKE)
#define SAM3X_CHIPID_CIDR 0x400E0940
#define SAM3N_CHIPID_CIDR 0x400E0740
#define SAM3S_CHIPID_CIDR 0x400E0740
#define SAM3U_CHIPID_CIDR 0x400E0740
#define SAM4S_CHIPID_CIDR 0x400E0740
#define SAM34NSU_CHIPID_CIDR 0x400E0740
#define CHIPID_CIDR_VERSION_MASK (0x1F << 0)
#define CHIPID_CIDR_EPROC_CM3 (0x03 << 5)
#define CHIPID_CIDR_EPROC_CM4 (0x07 << 5)
#define CHIPID_CIDR_EPROC_MASK (0x07 << 5)
#define CHIPID_CIDR_NVPSIZ_MASK (0x0F << 8)
#define CHIPID_CIDR_NVPSIZ_8K (0x01 << 8)
#define CHIPID_CIDR_NVPSIZ_16K (0x02 << 8)
#define CHIPID_CIDR_NVPSIZ_32K (0x03 << 8)
#define CHIPID_CIDR_NVPSIZ_64K (0x05 << 8)
#define CHIPID_CIDR_NVPSIZ_128K (0x07 << 8)
#define CHIPID_CIDR_NVPSIZ_256K (0x09 << 8)
#define CHIPID_CIDR_NVPSIZ_512K (0x0A << 8)
@ -162,70 +118,134 @@ static const char sam4s_xml_memory_map[] = "<?xml version=\"1.0\"?>"
#define SAM3_PAGE_SIZE 256
#define SAM4_PAGE_SIZE 512
struct sam_flash {
struct target_flash f;
uint32_t eefc_base;
uint8_t write_cmd;
};
static void sam3_add_flash(target *t,
uint32_t eefc_base, uint32_t addr, size_t length)
{
struct sam_flash *sf = calloc(1, sizeof(*sf));
struct target_flash *f = &sf->f;
f->start = addr;
f->length = length;
f->blocksize = SAM3_PAGE_SIZE;
f->erase = sam3_flash_erase;
f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered;
f->write_buf = sam3x_flash_write;
f->buf_size = SAM3_PAGE_SIZE;
sf->eefc_base = eefc_base;
sf->write_cmd = EEFC_FCR_FCMD_EWP;
target_add_flash(t, f);
}
static void sam4_add_flash(target *t,
uint32_t eefc_base, uint32_t addr, size_t length)
{
struct sam_flash *sf = calloc(1, sizeof(*sf));
struct target_flash *f = &sf->f;
f->start = addr;
f->length = length;
f->blocksize = SAM4_PAGE_SIZE * 8;
f->erase = sam4_flash_erase;
f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered;
f->write_buf = sam3x_flash_write;
f->buf_size = SAM4_PAGE_SIZE;
sf->eefc_base = eefc_base;
sf->write_cmd = EEFC_FCR_FCMD_WP;
target_add_flash(t, f);
}
static size_t sam_flash_size(uint32_t idcode)
{
switch (idcode & CHIPID_CIDR_NVPSIZ_MASK) {
case CHIPID_CIDR_NVPSIZ_8K:
return 0x2000;
case CHIPID_CIDR_NVPSIZ_16K:
return 0x4000;
case CHIPID_CIDR_NVPSIZ_32K:
return 0x8000;
case CHIPID_CIDR_NVPSIZ_64K:
return 0x10000;
case CHIPID_CIDR_NVPSIZ_128K:
return 0x20000;
case CHIPID_CIDR_NVPSIZ_256K:
return 0x40000;
case CHIPID_CIDR_NVPSIZ_512K:
return 0x80000;
case CHIPID_CIDR_NVPSIZ_1024K:
return 0x100000;
case CHIPID_CIDR_NVPSIZ_2048K:
return 0x200000;
}
return 0;
}
bool sam3x_probe(target *t)
{
t->idcode = target_mem_read32(t, SAM3X_CHIPID_CIDR);
/* FIXME: Check for all variants with similar flash interface */
size_t size = sam_flash_size(t->idcode);
switch (t->idcode & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) {
case CHIPID_CIDR_ARCH_SAM3XxC | CHIPID_CIDR_EPROC_CM3:
case CHIPID_CIDR_ARCH_SAM3XxE | CHIPID_CIDR_EPROC_CM3:
case CHIPID_CIDR_ARCH_SAM3XxG | CHIPID_CIDR_EPROC_CM3:
t->driver = "Atmel SAM3X";
t->xml_mem_map = sam3x_xml_memory_map;
t->flash_erase = sam3x_flash_erase;
t->flash_write = sam3x_flash_write;
target_add_ram(t, 0x20000000, 0x200000);
/* 2 Flash memories back-to-back starting at 0x80000 */
sam3_add_flash(t, SAM3X_EEFC_BASE(0), 0x80000, size/2);
sam3_add_flash(t, SAM3X_EEFC_BASE(1), 0x80000 + size/2, size/2);
target_add_commands(t, sam3x_cmd_list, "SAM3X");
return true;
}
t->idcode = target_mem_read32(t, SAM3N_CHIPID_CIDR);
t->idcode = target_mem_read32(t, SAM34NSU_CHIPID_CIDR);
size = sam_flash_size(t->idcode);
switch (t->idcode & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) {
case CHIPID_CIDR_ARCH_SAM3NxA | CHIPID_CIDR_EPROC_CM3:
case CHIPID_CIDR_ARCH_SAM3NxB | CHIPID_CIDR_EPROC_CM3:
case CHIPID_CIDR_ARCH_SAM3NxC | CHIPID_CIDR_EPROC_CM3:
t->driver = "Atmel SAM3N";
t->xml_mem_map = sam3n_xml_memory_map;
t->flash_erase = sam3x_flash_erase;
t->flash_write = sam3x_flash_write;
target_add_commands(t, sam3x_cmd_list, "SAM3N");
return true;
}
t->idcode = target_mem_read32(t, SAM3S_CHIPID_CIDR);
switch (t->idcode & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) {
case CHIPID_CIDR_ARCH_SAM3SxA | CHIPID_CIDR_EPROC_CM3:
case CHIPID_CIDR_ARCH_SAM3SxB | CHIPID_CIDR_EPROC_CM3:
case CHIPID_CIDR_ARCH_SAM3SxC | CHIPID_CIDR_EPROC_CM3:
t->driver = "Atmel SAM3S";
t->xml_mem_map = sam3n_xml_memory_map;
t->flash_erase = sam3x_flash_erase;
t->flash_write = sam3x_flash_write;
target_add_commands(t, sam3x_cmd_list, "SAM3S");
t->driver = "Atmel SAM3N/S";
target_add_ram(t, 0x20000000, 0x200000);
/* These devices only have a single bank */
size = sam_flash_size(t->idcode);
sam3_add_flash(t, SAM3N_EEFC_BASE, 0x400000, size);
target_add_commands(t, sam3x_cmd_list, "SAM3N/S");
return true;
}
t->idcode = target_mem_read32(t, SAM3U_CHIPID_CIDR);
switch (t->idcode & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) {
case CHIPID_CIDR_ARCH_SAM3UxC | CHIPID_CIDR_EPROC_CM3:
case CHIPID_CIDR_ARCH_SAM3UxE | CHIPID_CIDR_EPROC_CM3:
t->driver = "Atmel SAM3U";
t->xml_mem_map = sam3u_xml_memory_map;
t->flash_erase = sam3x_flash_erase;
t->flash_write = sam3x_flash_write;
target_add_ram(t, 0x20000000, 0x200000);
/* One flash up to 512K at 0x80000 */
sam3_add_flash(t, SAM3U_EEFC_BASE(0), 0x80000, MIN(size, 0x80000));
if (size >= 0x80000) {
/* Larger devices have a second bank at 0x100000 */
sam3_add_flash(t, SAM3U_EEFC_BASE(1),
0x100000, 0x80000);
}
target_add_commands(t, sam3x_cmd_list, "SAM3U");
return true;
}
t->idcode = target_mem_read32(t, SAM4S_CHIPID_CIDR);
switch (t->idcode & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) {
case CHIPID_CIDR_ARCH_SAM4SxA | CHIPID_CIDR_EPROC_CM4:
case CHIPID_CIDR_ARCH_SAM4SxB | CHIPID_CIDR_EPROC_CM4:
case CHIPID_CIDR_ARCH_SAM4SxC | CHIPID_CIDR_EPROC_CM4:
t->driver = "Atmel SAM4S";
t->xml_mem_map = sam4s_xml_memory_map;
t->flash_erase = sam3x_flash_erase;
t->flash_write = sam3x_flash_write;
target_add_ram(t, 0x20000000, 0x400000);
size_t size = sam_flash_size(t->idcode);
if (size <= 0x80000) {
/* Smaller devices have a single bank */
sam4_add_flash(t, SAM4S_EEFC_BASE(0), 0x400000, size);
} else {
/* Larger devices are split evenly between 2 */
sam4_add_flash(t, SAM4S_EEFC_BASE(0), 0x400000, size/2);
sam4_add_flash(t, SAM4S_EEFC_BASE(1),
0x400000 + size/2, size/2);
}
target_add_commands(t, sam3x_cmd_list, "SAM4S");
return true;
}
@ -249,191 +269,70 @@ sam3x_flash_cmd(target *t, uint32_t base, uint8_t cmd, uint16_t arg)
return sr & EEFC_FSR_ERROR;
}
static uint32_t
sam3x_flash_base(target *t, uint32_t addr, uint32_t *offset)
static uint32_t sam3x_flash_base(target *t)
{
if (strcmp(t->driver, "Atmel SAM3X") == 0) {
uint32_t half = -1;
switch (t->idcode & CHIPID_CIDR_NVPSIZ_MASK) {
case CHIPID_CIDR_NVPSIZ_128K:
half = 0x00090000;
break;
case CHIPID_CIDR_NVPSIZ_256K:
half = 0x000A0000;
break;
case CHIPID_CIDR_NVPSIZ_512K:
half = 0x000C0000;
break;
}
if (addr > half) {
if (offset)
*offset = addr - half;
return SAM3X_EEFC_BASE(1);
} else {
if (offset)
*offset = addr - 0x80000;
return SAM3X_EEFC_BASE(0);
}
return SAM3X_EEFC_BASE(0);
}
/* The SAM3U has a constant split between both banks */
if (strcmp(t->driver, "Atmel SAM3U") == 0) {
if (addr >= 0x100000) {
if(offset)
*offset = addr - 0x100000;
return SAM3U_EEFC_BASE(1);
} else {
if(offset)
*offset = addr - 0x80000;
return SAM3U_EEFC_BASE(0);
}
return SAM3U_EEFC_BASE(0);
}
if (strcmp(t->driver, "Atmel SAM4S") == 0) {
uint32_t half = -1;
switch (t->idcode & CHIPID_CIDR_NVPSIZ_MASK) {
case CHIPID_CIDR_NVPSIZ_128K:
case CHIPID_CIDR_NVPSIZ_256K:
case CHIPID_CIDR_NVPSIZ_512K:
if (offset)
*offset = addr - 0x400000;
return SAM4S_EEFC_BASE(0);
case CHIPID_CIDR_NVPSIZ_1024K:
half = 0x480000;
break;
case CHIPID_CIDR_NVPSIZ_2048K:
half = 0x500000;
break;
}
if (addr >= half) {
if (offset)
*offset = addr - half;
return SAM4S_EEFC_BASE(1);
} else {
if (offset)
*offset = addr - 0x400000;
return SAM4S_EEFC_BASE(0);
}
return SAM4S_EEFC_BASE(0);
}
/* SAM3N device */
if (offset)
*offset = addr - 0x400000;
return SAM3N_EEFC_BASE;
}
static int sam3x_flash_erase(target *t, uint32_t addr, size_t len)
static int sam4_flash_erase(struct target_flash *f, uint32_t addr, size_t len)
{
uint32_t offset;
uint32_t base = sam3x_flash_base(t, addr, &offset);
target *t = f->t;
uint32_t base = ((struct sam_flash *)f)->eefc_base;
uint32_t offset = addr - f->start;
/* The SAM4S is the only supported device with a page erase command.
* Erasing is done in 8-page chunks. arg[15:2] contains the page
* number and arg[1:0] contains 0x1, indicating 8-page chunks.
*/
if (strcmp(t->driver, "Atmel SAM4S") == 0) {
unsigned chunk = offset / SAM4_PAGE_SIZE;
unsigned chunk = offset / SAM4_PAGE_SIZE;
/* Fail if the start address is not 8-page-aligned. */
if (chunk % 8 != 0)
while (len) {
int16_t arg = chunk | 0x1;
if(sam3x_flash_cmd(t, base, EEFC_FCR_FCMD_EPA, arg))
return -1;
/* Note that the length might not be a multiple of 8 pages.
* In this case, we will erase a few extra pages at the end.
*/
while (len > 0) {
int16_t arg = chunk | 0x1;
if(sam3x_flash_cmd(t, base, EEFC_FCR_FCMD_EPA, arg))
return -1;
len -= SAM4_PAGE_SIZE * 8;
addr += SAM4_PAGE_SIZE * 8;
chunk += 8;
}
return 0;
len -= f->blocksize;
chunk += 8;
}
return 0;
}
static int sam3_flash_erase(struct target_flash *f, uint32_t addr, size_t len)
{
/* The SAM3X/SAM3N don't really have a page erase function.
* This Erase/Write page is the best we have, so we write with all
* ones. This does waste time, but what can we do?
* We do nothing here and use Erase/Write page in flash_write.
*/
unsigned chunk = offset / SAM3_PAGE_SIZE;
uint8_t buf[SAM3_PAGE_SIZE];
memset(buf, 0xff, sizeof(buf));
/* Only do this once, since it doesn't change. */
target_mem_write(t, addr, buf, SAM3_PAGE_SIZE);
while (len) {
if(sam3x_flash_cmd(t, base, EEFC_FCR_FCMD_EWP, chunk))
return -1;
len -= SAM3_PAGE_SIZE;
addr += SAM3_PAGE_SIZE;
chunk++;
}
(void)f; (void)addr; (void)len;
return 0;
}
static int sam3x_flash_write(target *t, uint32_t dest,
const uint8_t *src, size_t len)
static int sam3x_flash_write(struct target_flash *f, uint32_t dest,
const void *src, size_t len)
{
unsigned page_size;
if (strcmp(t->driver, "Atmel SAM4S") == 0) {
page_size = SAM4_PAGE_SIZE;
} else {
page_size = SAM3_PAGE_SIZE;
}
uint32_t offset;
uint32_t base = sam3x_flash_base(t, dest, &offset);
uint8_t buf[page_size];
unsigned first_chunk = offset / page_size;
unsigned last_chunk = (offset + len - 1) / page_size;
offset %= page_size;
dest -= offset;
for (unsigned chunk = first_chunk; chunk <= last_chunk; chunk++) {
DEBUG("chunk %u len %zu\n", chunk, len);
/* first and last chunk may require special handling */
if ((chunk == first_chunk) || (chunk == last_chunk)) {
/* fill with all ff to avoid sector rewrite corrupting other writes */
memset(buf, 0xff, sizeof(buf));
/* copy as much as fits */
size_t copylen = page_size - offset;
if (copylen > len)
copylen = len;
memcpy(&buf[offset], src, copylen);
/* update to suit */
len -= copylen;
src += copylen;
offset = 0;
} else {
target *t = f->t;
struct sam_flash *sf = (struct sam_flash *)f;
uint32_t base = sf->eefc_base;
unsigned chunk = (dest - f->start) / f->buf_size;
/* interior chunk, must be aligned and full-sized */
memcpy(buf, src, page_size);
len -= page_size;
src += page_size;
}
target_mem_write(t, dest, buf, page_size);
if(sam3x_flash_cmd(t, base, EEFC_FCR_FCMD_WP, chunk))
return -1;
}
target_mem_write(t, dest, src, len);
if(sam3x_flash_cmd(t, base, sf->write_cmd, chunk))
return -1;
return 0;
}
static bool sam3x_cmd_gpnvm_get(target *t)
{
uint32_t base = sam3x_flash_base(t, 0, NULL);
uint32_t base = sam3x_flash_base(t);
sam3x_flash_cmd(t, base, EEFC_FCR_FCMD_GGPB, 0);
gdb_outf("GPNVM: 0x%08X\n", target_mem_read32(t, EEFC_FRR(base)));
@ -444,7 +343,7 @@ static bool sam3x_cmd_gpnvm_get(target *t)
static bool sam3x_cmd_gpnvm_set(target *t, int argc, char *argv[])
{
uint32_t bit, cmd;
uint32_t base = sam3x_flash_base(t, 0, NULL);
uint32_t base = sam3x_flash_base(t);
if (argc != 3) {
gdb_out("usage: monitor gpnvm_set <bit> <val>\n");

111
src/samd.c

@ -40,9 +40,9 @@
#include "gdb_packet.h"
#include "cortexm.h"
static int samd_flash_erase(target *t, uint32_t addr, size_t len);
static int samd_flash_write(target *t, uint32_t dest,
const uint8_t *src, size_t len);
static int samd_flash_erase(struct target_flash *t, uint32_t addr, size_t len);
static int samd_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len);
static bool samd_cmd_erase_all(target *t);
static bool samd_cmd_lock_flash(target *t);
@ -63,21 +63,6 @@ const struct command_s samd_cmd_list[] = {
{NULL, NULL, NULL}
};
/**
* 256KB Flash Max., 32KB RAM Max. The smallest unit of erase is the
* one row = 256 bytes.
*/
static const char samd_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>"
" <memory type=\"flash\" start=\"0x0\" length=\"0x40000\">"
" <property name=\"blocksize\">0x100</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x8000\"/>"
"</memory-map>";
/* Non-Volatile Memory Controller (NVMC) Parameters */
#define SAMD_ROW_SIZE 256
#define SAMD_PAGE_SIZE 64
@ -361,6 +346,19 @@ struct samd_descr samd_parse_device_id(uint32_t did)
return samd;
}
static void samd_add_flash(target *t, uint32_t addr, size_t length)
{
struct target_flash *f = calloc(1, sizeof(*f));
f->start = addr;
f->length = length;
f->blocksize = SAMD_ROW_SIZE;
f->erase = samd_flash_erase;
f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered;
f->write_buf = samd_flash_write;
f->buf_size = SAMD_PAGE_SIZE;
target_add_flash(t, f);
}
char variant_string[40];
bool samd_probe(target *t)
@ -423,9 +421,8 @@ bool samd_probe(target *t)
t->attach = samd_protected_attach;
}
t->xml_mem_map = samd_xml_memory_map;
t->flash_erase = samd_flash_erase;
t->flash_write = samd_flash_write;
target_add_ram(t, 0x20000000, 0x8000);
samd_add_flash(t, 0x00000000, 0x40000);
target_add_commands(t, samd_cmd_list, "SAMD");
/* If we're not in reset here */
@ -463,11 +460,9 @@ static void samd_unlock_current_address(target *t)
/**
* Erase flash row by row
*/
static int samd_flash_erase(target *t, uint32_t addr, size_t len)
static int samd_flash_erase(struct target_flash *f, uint32_t addr, size_t len)
{
addr &= ~(SAMD_ROW_SIZE - 1);
len &= ~(SAMD_ROW_SIZE - 1);
target *t = f->t;
while (len) {
/* Write address of first word in row to erase it */
/* Must be shifted right for 16-bit address, see Datasheet §20.8.8 Address */
@ -487,8 +482,8 @@ static int samd_flash_erase(target *t, uint32_t addr, size_t len)
/* Lock */
samd_lock_current_address(t);
addr += SAMD_ROW_SIZE;
len -= SAMD_ROW_SIZE;
addr += f->blocksize;
len -= f->blocksize;
}
return 0;
@ -497,56 +492,28 @@ static int samd_flash_erase(target *t, uint32_t addr, size_t len)
/**
* Write flash page by page
*/
static int samd_flash_write(target *t, uint32_t dest,
const uint8_t *src, size_t len)
static int samd_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len)
{
/* Find the size of our 32-bit data buffer */
uint32_t offset = dest % 4;
uint32_t words = (offset + len + 3) / 4;
uint32_t data[words], i = 0;
/* Populate the data buffer */
memset((uint8_t *)data, 0xFF, words * 4);
memcpy((uint8_t *)data + offset, src, len);
/* The address of the first word involved in the write */
uint32_t addr = dest & ~0x3;
/* The address of the last word involved in the write */
uint32_t end = (dest + len - 1) & ~0x3;
/* The start address of the first page involved in the write */
uint32_t first_page = dest & ~(SAMD_PAGE_SIZE - 1);
/* The start address of the last page involved in the write */
uint32_t last_page = (dest + len - 1) & ~(SAMD_PAGE_SIZE - 1);
uint32_t next_page;
uint32_t length;
for (uint32_t page = first_page; page <= last_page; page += SAMD_PAGE_SIZE) {
next_page = page + SAMD_PAGE_SIZE;
length = MIN(end + 4, next_page) - addr;
/* Write within a single page. This may be part or all of the page */
target_mem_write(t, addr, &data[i], length);
addr += length; i += (length >> 2);
/* If MANW=0 (default) we may have triggered an automatic
* write. Ignore this */
target *t = f->t;
/* Unlock */
samd_unlock_current_address(t);
/* Write within a single page. This may be part or all of the page */
target_mem_write(t, dest, src, len);
/* Issue the write page command */
target_mem_write32(t, SAMD_NVMC_CTRLA,
SAMD_CTRLA_CMD_KEY | SAMD_CTRLA_CMD_WRITEPAGE);
/* Unlock */
samd_unlock_current_address(t);
/* Poll for NVM Ready */
while ((target_mem_read32(t, SAMD_NVMC_INTFLAG) & SAMD_NVMC_READY) == 0)
if (target_check_error(t))
return -1;
/* Issue the write page command */
target_mem_write32(t, SAMD_NVMC_CTRLA,
SAMD_CTRLA_CMD_KEY | SAMD_CTRLA_CMD_WRITEPAGE);
/* Lock */
samd_lock_current_address(t);
}
/* Poll for NVM Ready */
while ((target_mem_read32(t, SAMD_NVMC_INTFLAG) & SAMD_NVMC_READY) == 0)
if (target_check_error(t))
return -1;
/* Lock */
samd_lock_current_address(t);
return 0;
}

139
src/stm32f1.c

@ -46,43 +46,10 @@ const struct command_s stm32f1_cmd_list[] = {
};
static int stm32md_flash_erase(target *t, uint32_t addr, size_t len);
static int stm32hd_flash_erase(target *t, uint32_t addr, size_t len);
static int stm32f1_flash_erase(target *t, uint32_t addr, size_t len,
uint32_t pagesize);
static int stm32f1_flash_write(target *t, uint32_t dest,
const uint8_t *src, size_t len);
static const char stm32f1_driver_str[] = "STM32, Medium density.";
static const char stm32hd_driver_str[] = "STM32, High density.";
static const char stm32f3_driver_str[] = "STM32F3xx";
static const char stm32f03_driver_str[] = "STM32F03x";
static const char stm32f04_driver_str[] = "STM32F04x";
static const char stm32f05_driver_str[] = "STM32F05x";
static const char stm32f07_driver_str[] = "STM32F07x";
static const char stm32f09_driver_str[] = "STM32F09x";
static const char stm32f1_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>"
" <memory type=\"flash\" start=\"0x8000000\" length=\"0x20000\">"
" <property name=\"blocksize\">0x400</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x5000\"/>"
"</memory-map>";
static const char stm32hd_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>"
" <memory type=\"flash\" start=\"0x8000000\" length=\"0x80000\">"
" <property name=\"blocksize\">0x800</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x10000\"/>"
"</memory-map>";
static int stm32f1_flash_erase(struct target_flash *f,
uint32_t addr, size_t len);
static int stm32f1_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len);
/* Flash Program ad Erase Controller Register Map */
#define FPEC_BASE 0x40022000
@ -127,6 +94,20 @@ static const uint16_t stm32f1_flash_write_stub[] = {
#define SRAM_BASE 0x20000000
#define STUB_BUFFER_BASE ALIGN(SRAM_BASE + sizeof(stm32f1_flash_write_stub), 4)
static void stm32f1_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 = stm32f1_flash_erase;
f->write = stm32f1_flash_write;
f->align = 2;
f->erased = 0xff;
target_add_flash(t, f);
}
bool stm32f1_probe(target *t)
{
t->idcode = target_mem_read32(t, DBGMCU_IDCODE) & 0xfff;
@ -134,27 +115,24 @@ bool stm32f1_probe(target *t)
case 0x410: /* Medium density */
case 0x412: /* Low denisty */
case 0x420: /* Value Line, Low-/Medium density */
t->driver = stm32f1_driver_str;
t->xml_mem_map = stm32f1_xml_memory_map;
t->flash_erase = stm32md_flash_erase;
t->flash_write = stm32f1_flash_write;
t->driver = "STM32F1 medium density";
target_add_ram(t, 0x20000000, 0x5000);
stm32f1_add_flash(t, 0x8000000, 0x20000, 0x400);
target_add_commands(t, stm32f1_cmd_list, "STM32 LD/MD");
return true;
case 0x414: /* High density */
case 0x418: /* Connectivity Line */
case 0x428: /* Value Line, High Density */
t->driver = stm32hd_driver_str;
t->xml_mem_map = stm32hd_xml_memory_map;
t->flash_erase = stm32hd_flash_erase;
t->flash_write = stm32f1_flash_write;
t->driver = "STM32F1 high density";
target_add_ram(t, 0x20000000, 0x10000);
stm32f1_add_flash(t, 0x8000000, 0x80000, 0x800);
target_add_commands(t, stm32f1_cmd_list, "STM32 HD/CL");
return true;
case 0x422: /* STM32F30x */
case 0x432: /* STM32F37x */
t->driver = stm32f3_driver_str;
t->xml_mem_map = stm32hd_xml_memory_map;
t->flash_erase = stm32hd_flash_erase;
t->flash_write = stm32f1_flash_write;
t->driver = "STM32F3";
target_add_ram(t, 0x20000000, 0x10000);
stm32f1_add_flash(t, 0x8000000, 0x80000, 0x800);
target_add_commands(t, stm32f1_cmd_list, "STM32F3");
return true;
}
@ -166,26 +144,9 @@ bool stm32f1_probe(target *t)
case 0x440: /* STM32F05 RM0091 Rev.7 */
case 0x448: /* STM32F07 RM0091 Rev.7 */
case 0x442: /* STM32F09 RM0091 Rev.7 */
switch(t->idcode) {
case 0x444: /* STM32F03 */
t->driver = stm32f03_driver_str;
break;
case 0x445: /* STM32F04 */
t->driver = stm32f04_driver_str;
break;
case 0x440: /* STM32F05 */
t->driver = stm32f05_driver_str;
break;
case 0x448: /* STM32F07 */
t->driver = stm32f07_driver_str;
break;
case 0x442: /* STM32F09 */
t->driver = stm32f09_driver_str;
break;
}
t->xml_mem_map = stm32f1_xml_memory_map;
t->flash_erase = stm32md_flash_erase;
t->flash_write = stm32f1_flash_write;
t->driver = "STM32F0";
target_add_ram(t, 0x20000000, 0x5000);
stm32f1_add_flash(t, 0x8000000, 0x20000, 0x400);
target_add_commands(t, stm32f1_cmd_list, "STM32F0");
return true;
}
@ -199,14 +160,12 @@ static void stm32f1_flash_unlock(target *t)
target_mem_write32(t, FLASH_KEYR, KEY2);
}
static int stm32f1_flash_erase(target *t, uint32_t addr,
size_t len, uint32_t pagesize)
static int stm32f1_flash_erase(struct target_flash *f,
uint32_t addr, size_t len)
{
target *t = f->t;
uint16_t sr;
addr &= ~(pagesize - 1);
len = (len + pagesize - 1) & ~(pagesize - 1);
stm32f1_flash_unlock(t);
while(len) {
@ -222,8 +181,8 @@ static int stm32f1_flash_erase(target *t, uint32_t addr,
if(target_check_error(t))
return -1;
len -= pagesize;
addr += pagesize;
len -= f->blocksize;
addr += f->blocksize;
}
/* Check for error */
@ -234,33 +193,15 @@ static int stm32f1_flash_erase(target *t, uint32_t addr,
return 0;
}
static int stm32hd_flash_erase(target *t, uint32_t addr, size_t len)
{
return stm32f1_flash_erase(t, addr, len, 0x800);
}
static int stm32md_flash_erase(target *t, uint32_t addr, size_t len)
{
return stm32f1_flash_erase(t, addr, len, 0x400);
}
static int stm32f1_flash_write(target *t, uint32_t dest,
const uint8_t *src, size_t len)
static int stm32f1_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len)
{
uint32_t offset = dest % 4;
uint8_t data[ALIGN(offset + len, 4)];
/* Construct data buffer used by stub */
/* pad partial words with all 1s to avoid damaging overlapping areas */
memset(data, 0xff, sizeof(data));
memcpy((uint8_t *)data + offset, src, len);
target *t = f->t;
/* Write stub and data to target ram and set PC */
target_mem_write(t, SRAM_BASE, stm32f1_flash_write_stub,
sizeof(stm32f1_flash_write_stub));
target_mem_write(t, STUB_BUFFER_BASE, data, sizeof(data));
return cortexm_run_stub(t, SRAM_BASE, dest - offset,
STUB_BUFFER_BASE, sizeof(data), 0);
target_mem_write(t, STUB_BUFFER_BASE, src, len);
return cortexm_run_stub(t, SRAM_BASE, dest, STUB_BUFFER_BASE, len, 0);
}
static bool stm32f1_cmd_erase_mass(target *t)

118
src/stm32f4.c

@ -47,39 +47,12 @@ const struct command_s stm32f4_cmd_list[] = {
};
static int stm32f4_flash_erase(target *t, uint32_t addr, size_t len);
static int stm32f4_flash_write(target *t, uint32_t dest,
const uint8_t *src, size_t len);
static int stm32f4_flash_erase(struct target_flash *f, uint32_t addr, size_t len);
static int stm32f4_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len);
static const char stm32f4_driver_str[] = "STM32F4xx";
static const char stm32f4_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>"
" <memory type=\"flash\" start=\"0x8000000\" length=\"0x10000\">"
" <property name=\"blocksize\">0x4000</property>"
" </memory>"
" <memory type=\"flash\" start=\"0x8010000\" length=\"0x10000\">"
" <property name=\"blocksize\">0x10000</property>"
" </memory>"
" <memory type=\"flash\" start=\"0x8020000\" length=\"0xE0000\">"
" <property name=\"blocksize\">0x20000</property>"
" </memory>"
" <memory type=\"flash\" start=\"0x8100000\" length=\"0x10000\">"
" <property name=\"blocksize\">0x4000</property>"
" </memory>"
" <memory type=\"flash\" start=\"0x8110000\" length=\"0x10000\">"
" <property name=\"blocksize\">0x10000</property>"
" </memory>"
" <memory type=\"flash\" start=\"0x8120000\" length=\"0xE0000\">"
" <property name=\"blocksize\">0x20000</property>"
" </memory>"
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x30000\"/>"
" <memory type=\"ram\" start=\"0x10000000\" length=\"0x10000\"/>"
"</memory-map>";
/* Flash Program ad Erase Controller Register Map */
#define FPEC_BASE 0x40023C00
#define FLASH_ACR (FPEC_BASE+0x00)
@ -127,22 +100,51 @@ static const uint16_t stm32f4_flash_write_stub[] = {
#define SRAM_BASE 0x20000000
#define STUB_BUFFER_BASE ALIGN(SRAM_BASE + sizeof(stm32f4_flash_write_stub), 4)
struct stm32f4_flash {
struct target_flash f;
uint8_t base_sector;
};
static void stm32f4_add_flash(target *t,
uint32_t addr, size_t length, size_t blocksize,
uint8_t base_sector)
{
struct stm32f4_flash *sf = calloc(1, sizeof(*sf));
struct target_flash *f = &sf->f;
f->start = addr;
f->length = length;
f->blocksize = blocksize;
f->erase = stm32f4_flash_erase;
f->write = stm32f4_flash_write;
f->align = 4;
f->erased = 0xff;
sf->base_sector = base_sector;
target_add_flash(t, f);
}
bool stm32f4_probe(target *t)
{
uint32_t idcode;
idcode = target_mem_read32(t, DBGMCU_IDCODE);
switch(idcode & 0xFFF) {
case 0x419: /* 427/437 */
/* Second bank for 2M parts. */
stm32f4_add_flash(t, 0x8100000, 0x10000, 0x4000, 12);
stm32f4_add_flash(t, 0x8110000, 0x10000, 0x10000, 16);
stm32f4_add_flash(t, 0x8120000, 0xE0000, 0x20000, 17);
/* Fall through for stuff common to F40x/F41x */
case 0x411: /* Documented to be 0x413! This is what I read... */
case 0x413: /* F407VGT6 */
case 0x419: /* 427/437 */
case 0x423: /* F401 B/C RM0368 Rev.3 */
case 0x431: /* F411 RM0383 Rev.4 */
case 0x433: /* F401 D/E RM0368 Rev.3 */
t->xml_mem_map = stm32f4_xml_memory_map;
t->driver = stm32f4_driver_str;
t->flash_erase = stm32f4_flash_erase;
t->flash_write = stm32f4_flash_write;
target_add_ram(t, 0x10000000, 0x10000);
target_add_ram(t, 0x20000000, 0x30000);
stm32f4_add_flash(t, 0x8000000, 0x10000, 0x4000, 0);
stm32f4_add_flash(t, 0x8010000, 0x10000, 0x10000, 4);
stm32f4_add_flash(t, 0x8020000, 0xE0000, 0x20000, 5);
target_add_commands(t, stm32f4_cmd_list, "STM32F4");
return true;
}
@ -158,30 +160,18 @@ static void stm32f4_flash_unlock(target *t)
}
}
static int stm32f4_flash_erase(target *t, uint32_t addr, size_t len)
static int stm32f4_flash_erase(struct target_flash *f, uint32_t addr, size_t len)
{
target *t = f->t;
uint16_t sr;
uint32_t cr;
uint32_t pagesize;
addr &= 0x07FFC000;
uint8_t sector = ((struct stm32f4_flash *)f)->base_sector +
(addr - f->start)/f->blocksize;
stm32f4_flash_unlock(t);
while(len) {
if (addr < 0x10000) { /* Sector 0..3 */
cr = (addr >> 11);
pagesize = 0x4000;
} else if (addr < 0x20000) { /* Sector 4 */
cr = (4 << 3);
pagesize = 0x10000;
} else if (addr < 0x100000) { /* Sector 5..11 */
cr = (((addr - 0x20000) >> 14) + 0x28);
pagesize = 0x20000;
} else { /* Sector > 11 ?? */
return -1;
}
cr |= FLASH_CR_EOPIE | FLASH_CR_ERRIE | FLASH_CR_SER;
uint32_t cr = FLASH_CR_EOPIE | FLASH_CR_ERRIE | FLASH_CR_SER |
(sector << 3);
/* Flash page erase instruction */
target_mem_write32(t, FLASH_CR, cr);
/* write address to FMA */
@ -192,8 +182,8 @@ static int stm32f4_flash_erase(target *t, uint32_t addr, size_t len)
if(target_check_error(t))
return -1;
len -= pagesize;
addr += pagesize;
len -= f->blocksize;
sector++;
}
/* Check for error */
@ -204,23 +194,15 @@ static int stm32f4_flash_erase(target *t, uint32_t addr, size_t len)
return 0;
}
static int stm32f4_flash_write(target *t, uint32_t dest,
const uint8_t *src, size_t len)
static int stm32f4_flash_write(struct target_flash *f,
uint32_t dest, const void *src, size_t len)
{
uint32_t offset = dest % 4;
uint8_t data[ALIGN(offset + len, 4)];
/* Construct data buffer used by stub */
/* pad partial words with all 1s to avoid damaging overlapping areas */
memset(data, 0xff, sizeof(data));
memcpy((uint8_t *)data + offset, src, len);
/* Write buffer to target ram call stub */
target_mem_write(t, SRAM_BASE, stm32f4_flash_write_stub,
target_mem_write(f->t, SRAM_BASE, stm32f4_flash_write_stub,
sizeof(stm32f4_flash_write_stub));
target_mem_write(t, STUB_BUFFER_BASE, data, sizeof(data));
return cortexm_run_stub(t, SRAM_BASE, dest - offset,
STUB_BUFFER_BASE, sizeof(data), 0);
target_mem_write(f->t, STUB_BUFFER_BASE, src, len);
return cortexm_run_stub(f->t, SRAM_BASE, dest,
STUB_BUFFER_BASE, len, 0);
}
static bool stm32f4_cmd_erase_mass(target *t)

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);

176
src/target.c

@ -48,6 +48,20 @@ void target_list_free(void)
free(target_list->commands);
target_list->commands = tc;
}
if (target_list->dyn_mem_map)
free(target_list->dyn_mem_map);
while (target_list->ram) {
void * next = target_list->ram->next;
free(target_list->ram);
target_list->ram = next;
}
while (target_list->flash) {
void * next = target_list->flash->next;
if (target_list->flash->buf)
free(target_list->flash->buf);
free(target_list->flash);
target_list->flash = next;
}
free(target_list);
target_list = t;
}
@ -80,3 +94,165 @@ target *target_attach(target *t, target_destroy_callback destroy_cb)
return t;
}
void target_add_ram(target *t, uint32_t start, uint32_t len)
{
struct target_ram *ram = malloc(sizeof(*ram));
ram->start = start;
ram->length = len;
ram->next = t->ram;
t->ram = ram;
}
void target_add_flash(target *t, struct target_flash *f)
{
f->t = t;
f->next = t->flash;
t->flash = f;
}
static ssize_t map_ram(char *buf, size_t len, struct target_ram *ram)
{
return snprintf(buf, len, "<memory type=\"ram\" start=\"0x%08"PRIx32
"\" length=\"0x%08"PRIx32"\"/>",
ram->start, ram->length);
}
static ssize_t map_flash(char *buf, size_t len, struct target_flash *f)
{
int i = 0;
i += snprintf(&buf[i], len - i, "<memory type=\"flash\" start=\"0x%08"PRIx32
"\" length=\"0x%08"PRIx32"\">",
f->start, f->length);
i += snprintf(&buf[i], len - i, "<property name=\"blocksize\">0x%08"PRIx32
"</property></memory>",
f->blocksize);
return i;
}
const char *target_mem_map(target *t)
{
if (t->dyn_mem_map)
return t->dyn_mem_map;
/* FIXME size buffer */
size_t len = 1024;
char *tmp = malloc(len);
size_t i = 0;
i = snprintf(&tmp[i], len - i, "<memory-map>");
/* Map each defined RAM */
for (struct target_ram *r = t->ram; r; r = r->next)
i += map_ram(&tmp[i], len - i, r);
/* Map each defined Flash */
for (struct target_flash *f = t->flash; f; f = f->next)
i += map_flash(&tmp[i], len - i, f);
i += snprintf(&tmp[i], len - i, "</memory-map>");
t->dyn_mem_map = tmp;
return t->dyn_mem_map;
}
static struct target_flash *flash_for_addr(target *t, uint32_t addr)
{
for (struct target_flash *f = t->flash; f; f = f->next)
if ((f->start <= addr) &&
(addr < (f->start + f->length)))
return f;
return NULL;
}
int target_flash_erase(target *t, uint32_t addr, size_t len)
{
int ret = 0;
while (len) {
struct target_flash *f = flash_for_addr(t, addr);
size_t tmplen = MIN(len, f->length - (addr % f->length));
ret |= f->erase(f, addr, tmplen);
addr += tmplen;
len -= tmplen;
}
return ret;
}
int target_flash_write(target *t,
uint32_t dest, const void *src, size_t len)
{
int ret = 0;
while (len) {
struct target_flash *f = flash_for_addr(t, dest);
size_t tmplen = MIN(len, f->length - (dest % f->length));
if (f->align > 1) {
uint32_t offset = dest % f->align;
uint8_t data[ALIGN(offset + len, f->align)];
memset(data, f->erased, sizeof(data));
memcpy((uint8_t *)data + offset, src, len);
ret |= f->write(f, dest - offset, data, sizeof(data));
} else {
ret |= f->write(f, dest, src, tmplen);
}
src += tmplen;
len -= tmplen;
}
return ret;
}
int target_flash_done(target *t)
{
for (struct target_flash *f = t->flash; f; f = f->next) {
if (f->done) {
int tmp = f->done(f);
if (tmp)
return tmp;
}
}
return 0;
}
int target_flash_write_buffered(struct target_flash *f,
uint32_t dest, const void *src, size_t len)
{
int ret = 0;
if (f->buf == NULL) {
/* Allocate flash sector buffer */
f->buf = malloc(f->buf_size);
f->buf_addr = -1;
}
while (len) {
uint32_t offset = dest % f->buf_size;
uint32_t base = dest - offset;
if (base != f->buf_addr) {
if (f->buf_addr != (uint32_t)-1) {
/* Write sector to flash if valid */
ret |= f->write_buf(f, f->buf_addr,
f->buf, f->buf_size);
}
/* Setup buffer for a new sector */
f->buf_addr = base;
memset(f->buf, f->erased, f->buf_size);
}
/* Copy chunk into sector buffer */
size_t sectlen = MIN(f->buf_size - offset, len);
memcpy(f->buf + offset, src, sectlen);
dest += sectlen;
src += sectlen;
len -= sectlen;
}
return ret;
}
int target_flash_done_buffered(struct target_flash *f)
{
int ret = 0;
if ((f->buf != NULL) &&(f->buf_addr != (uint32_t)-1)) {
/* Write sector to flash if valid */
ret = f->write_buf(f, f->buf_addr, f->buf, f->buf_size);
f->buf_addr = -1;
free(f->buf);
f->buf = NULL;
}
return ret;
}

Loading…
Cancel
Save