From 2336a313e4017af15c9bd76f6023799402de1d10 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Fri, 21 Aug 2015 16:57:10 +0200 Subject: [PATCH] Add STM32L4 handling. No option write handling yet. --- flashstub/Makefile | 6 +- flashstub/stm32l4.c | 53 ++++++++ flashstub/stm32l4.stub | 1 + src/Makefile | 1 + src/cortexm.c | 1 + src/include/target.h | 1 + src/stm32l4.c | 288 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 flashstub/stm32l4.c create mode 100644 flashstub/stm32l4.stub create mode 100644 src/stm32l4.c diff --git a/flashstub/Makefile b/flashstub/Makefile index faee76a..f6c6738 100644 --- a/flashstub/Makefile +++ b/flashstub/Makefile @@ -11,7 +11,7 @@ endif CFLAGS=-Os -std=gnu99 -mcpu=cortex-m0 -mthumb -I../libopencm3/include ASFLAGS=-mcpu=cortex-m3 -mthumb -all: lmi.stub stm32f4.stub nrf51.stub stm32f1.stub +all: lmi.stub stm32f4.stub stm32l4.stub nrf51.stub stm32f1.stub stm32f1.o: stm32f1.c $(Q)echo " CC $<" @@ -21,6 +21,10 @@ stm32f4.o: stm32f4.c $(Q)echo " CC $<" $(Q)$(CC) $(CFLAGS) -DSTM32F4 -o $@ -c $< +stm32l4.o: stm32l4.c + $(Q)echo " CC $<" + $(Q)$(CC) $(CFLAGS) -o $@ -c $< + %.o: %.s $(Q)echo " AS $<" $(Q)$(AS) $(ASFLAGS) -o $@ $< diff --git a/flashstub/stm32l4.c b/flashstub/stm32l4.c new file mode 100644 index 0000000..820bca4 --- /dev/null +++ b/flashstub/stm32l4.c @@ -0,0 +1,53 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2015 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * 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 . + */ +#include "stub.h" +#include + +/* No STM32L4 definitions in libopencm3 yet */ +#define FLASH_SR ((volatile uint32_t *) 0x40022010) +#define FLASH_SR_EOP (1 << 0) +#define SR_ERROR_MASK 0xC3FA +#define FLASH_SR_BSY (1 << 16) + +#define FLASH_CR ((volatile uint32_t *) 0x40022014) +#define FLASH_CR_PG (1 << 0) +#define FLASH_CR_EOPIE (1 << 24) +#define FLASH_CR_ERRIE (1 << 25) +#define FLASH_SR_EOP (1 << 0) + +void __attribute__((naked)) +stm32l4_flash_write_stub(uint32_t *dest, uint32_t *src, uint32_t size) +{ + if ((size & 7) || ((uint32_t)dest & 7)) + stub_exit(1); + for (int i = 0; i < size; i += 8) { + *FLASH_CR = FLASH_CR_EOPIE | FLASH_CR_ERRIE | FLASH_CR_PG; + *dest++ = *src++; + *dest++ = *src++; + __asm("dsb"); + while (*FLASH_SR & FLASH_SR_BSY) + ; + if ((*FLASH_SR & SR_ERROR_MASK) || !(*FLASH_SR & FLASH_SR_EOP)) + stub_exit(1); + *FLASH_SR |= FLASH_SR_EOP; + } + *FLASH_CR = 0; + stub_exit(0); +} diff --git a/flashstub/stm32l4.stub b/flashstub/stm32l4.stub new file mode 100644 index 0000000..73cd969 --- /dev/null +++ b/flashstub/stm32l4.stub @@ -0,0 +1 @@ +0x1C04, 0x4314, 0x2300, 0x0764, 0xD011, 0xBE01, 0xE00F, 0x4C11, 0x6825, 0x03ED, 0xD4FB, 0x6826, 0x4D0F, 0x422E, 0xD115, 0x6825, 0x07ED, 0xD512, 0x2601, 0x6825, 0x3308, 0x4335, 0x6025, 0x4C0B, 0x4293, 0xD20C, 0x4D0A, 0x6025, 0x58CC, 0x50C4, 0x18CC, 0x6865, 0x18C4, 0x6065, 0xF3BF, 0x8F4F, 0xE7E1, 0xBE01, 0xE7EA, 0x2300, 0x6023, 0xBE00, 0x2010, 0x4002, 0xC3FA, 0x0000, 0x2014, 0x4002, 0x0001, 0x0300, \ No newline at end of file diff --git a/src/Makefile b/src/Makefile index 856baee..1ef3486 100644 --- a/src/Makefile +++ b/src/Makefile @@ -41,6 +41,7 @@ SRC = \ stm32f1.c \ stm32f4.c \ stm32l0.c \ + stm32l4.c \ swdptap.c \ target.c \ diff --git a/src/cortexm.c b/src/cortexm.c index 028af21..60d3687 100644 --- a/src/cortexm.c +++ b/src/cortexm.c @@ -242,6 +242,7 @@ bool cortexm_probe(target *t) PROBE(stm32f1_probe); PROBE(stm32f4_probe); PROBE(stm32l0_probe); /* STM32L0xx & STM32L1xx */ + PROBE(stm32l4_probe); PROBE(lpc11xx_probe); PROBE(lpc43xx_probe); PROBE(sam3x_probe); diff --git a/src/include/target.h b/src/include/target.h index f86714b..bff556d 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -263,6 +263,7 @@ bool stm32f1_probe(target *t); bool stm32f4_probe(target *t); bool stm32l0_probe(target *t); bool stm32l1_probe(target *t); +bool stm32l4_probe(target *t); bool lmi_probe(target *t); bool lpc11xx_probe(target *t); bool lpc43xx_probe(target *t); diff --git a/src/stm32l4.c b/src/stm32l4.c new file mode 100644 index 0000000..d4e22a7 --- /dev/null +++ b/src/stm32l4.c @@ -0,0 +1,288 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2015 Uwe Bonnes + * Written by Uwe Bonnes + * + * 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 . + */ + +/* This file implements STM32L4 target specific functions for detecting + * the device, providing the XML memory map and Flash memory programming. + * + * On L4, flash and options are written in DWORDs (8-Byte) only. + * + * Refereces: + * ST doc - RM0351 + * Reference manual + * STM32L4x6 advanced ARMĀ®-based 32-bit MCUs + * + */ + +#include "general.h" +#include "adiv5.h" +#include "target.h" +#include "cortexm.h" +#include "command.h" +#include "gdb_packet.h" + +static bool stm32l4_cmd_erase_mass(target *t); +static bool stm32l4_cmd_erase_bank1(target *t); +static bool stm32l4_cmd_erase_bank2(target *t); +static bool stm32l4_cmd_option(target *t, int argc, char *argv[]); + +const struct command_s stm32l4_cmd_list[] = { + {"erase_mass", (cmd_handler)stm32l4_cmd_erase_mass, "Erase entire flash memory"}, + {"erase_bank1", (cmd_handler)stm32l4_cmd_erase_bank1, "Erase entire bank1 flash memory"}, + {"erase_bank2", (cmd_handler)stm32l4_cmd_erase_bank2, "Erase entire bank2 flash memory"}, + {"option", (cmd_handler)stm32l4_cmd_option, "Manipulate option bytes"}, + {NULL, NULL, NULL} +}; + + +static int stm32l4_flash_erase(struct target_flash *f, uint32_t addr, size_t len); +static int stm32l4_flash_write(struct target_flash *f, + uint32_t dest, const void *src, size_t len); + +static const char stm32l4_driver_str[] = "STM32L4xx"; + +#define PAGE_SIZE 0x800 +/* Flash Program ad Erase Controller Register Map */ +#define FPEC_BASE 0x40022000 +#define FLASH_ACR (FPEC_BASE+0x00) +#define FLASH_KEYR (FPEC_BASE+0x08) +#define FLASH_OPTKEYR (FPEC_BASE+0x0c) +#define FLASH_SR (FPEC_BASE+0x10) +#define FLASH_CR (FPEC_BASE+0x14) +#define FLASH_OPTR (FPEC_BASE+0x20) +//#define FLASH_OPTCR (FPEC_BASE+0x14) + +#define FLASH_CR_PG (1 << 0) +#define FLASH_CR_PER (1 << 1) +#define FLASH_CR_MER1 (1 << 2) +#define FLASH_CR_PAGE_SHIFT 3 +#define FLASH_CR_BKER (1 << 11) +#define FLASH_CR_MER2 (1 << 15) +#define FLASH_CR_STRT (1 << 16) +#define FLASH_CR_OPTSTRT (1 << 17) +#define FLASH_CR_FSTPG (1 << 18) +#define FLASH_CR_EOPIE (1 << 24) +#define FLASH_CR_ERRIE (1 << 25) +#define FLASH_CR_OPTLOCK (1 << 30) +#define FLASH_CR_LOCK (1 << 31) + +#define FLASH_SR_EOP (1 << 0) +#define FLASH_SR_OPERR (1 << 1) +#define FLASH_SR_PROGERR (1 << 3) +#define FLASH_SR_WRPERR (1 << 4) +#define FLASH_SR_PGAERR (1 << 5) +#define FLASH_SR_SIZERR (1 << 6) +#define FLASH_SR_PGSERR (1 << 7) +#define FLASH_SR_MSERR (1 << 8) +#define FLASH_SR_FASTERR (1 << 9) +#define FLASH_SR_RDERR (1 << 14) +#define FLASH_SR_OPTVERR (1 << 15) +#define FLASH_SR_ERROR_MASK 0xC3FA +#define FLASH_SR_BSY (1 << 16) + +#define KEY1 0x45670123 +#define KEY2 0xCDEF89AB + +#define OPTKEY1 0x08192A3B +#define OPTKEY2 0x4C5D6E7F + +#define SR_ERROR_MASK 0xF2 + +#define OR_DUALBANK (1 << 21) + +#define DBGMCU_IDCODE 0xE0042000 +#define FLASH_SIZE_REG 0x1FFF75E0 +#define FLASH_SIZE_REG 0x1FFF75E0 + +/* This routine is uses double word access.*/ +static const uint16_t stm32l4_flash_write_stub[] = { +#include "../flashstub/stm32l4.stub" +}; + +#define SRAM_BASE 0x20000000 +#define STUB_BUFFER_BASE ALIGN(SRAM_BASE + sizeof(stm32l4_flash_write_stub), 8) + +struct stm32l4_flash { + struct target_flash f; + uint32_t bank1_start; +}; + +static void stm32l4_add_flash(target *t, + uint32_t addr, size_t length, size_t blocksize, + uint32_t bank1_start) +{ + struct stm32l4_flash *sf = calloc(1, sizeof(*sf)); + struct target_flash *f = &sf->f; + f->start = addr; + f->length = length; + f->blocksize = blocksize; + f->erase = stm32l4_flash_erase; + f->write = stm32l4_flash_write; + f->align = 8; + f->erased = 0xff; + sf->bank1_start = bank1_start; + target_add_flash(t, f); +} + +bool stm32l4_probe(target *t) +{ + uint32_t idcode; + uint32_t size; + uint32_t options; + uint32_t bank1_start = 0x08040000; + + idcode = target_mem_read32(t, DBGMCU_IDCODE); + switch(idcode & 0xFFF) { + case 0x415: /* */ + t->driver = stm32l4_driver_str; + target_add_ram(t, 0x10000000, 1 << 15); + target_add_ram(t, 0x20000000, 3 << 15); + size = (target_mem_read32(t, FLASH_SIZE_REG) & 0xffff); + options = target_mem_read32(t, FLASH_OPTR); + if ((size < 0x400) && (options & OR_DUALBANK)) + bank1_start = 0x08000000 + (size << 9); + stm32l4_add_flash(t, 0x08000000, size << 10, PAGE_SIZE, bank1_start); + target_add_commands(t, stm32l4_cmd_list, "STM32L4"); + return true; + } + return false; +} + +static void stm32l4_flash_unlock(target *t) +{ + if (target_mem_read32(t, FLASH_CR) & FLASH_CR_LOCK) { + /* Enable FPEC controller access */ + target_mem_write32(t, FLASH_KEYR, KEY1); + target_mem_write32(t, FLASH_KEYR, KEY2); + } +} + +static int stm32l4_flash_erase(struct target_flash *f, uint32_t addr, size_t len) +{ + target *t = f->t; + uint16_t sr; + uint32_t bank1_start = ((struct stm32l4_flash *)f)->bank1_start; + uint32_t page; + + stm32l4_flash_unlock(t); + + page = (addr - 0x08000000) / PAGE_SIZE; + while(len) { + uint32_t cr; + + cr = FLASH_CR_PER | (page << FLASH_CR_PAGE_SHIFT ); + if (addr >= bank1_start) + cr |= FLASH_CR_BKER; + /* Flash page erase instruction */ + target_mem_write32(t, FLASH_CR, cr); + /* write address to FMA */ + cr |= FLASH_CR_STRT; + target_mem_write32(t, FLASH_CR, cr); + + /* Read FLASH_SR to poll for BSY bit */ + while(target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY) + if(target_check_error(t)) + return -1; + + len -= PAGE_SIZE; + addr += PAGE_SIZE; + page++; + } + + /* Check for error */ + sr = target_mem_read32(t, FLASH_SR); + if(sr & FLASH_SR_ERROR_MASK) + return -1; + + return 0; +} + +static int stm32l4_flash_write(struct target_flash *f, + uint32_t dest, const void *src, size_t len) +{ + /* Write buffer to target ram call stub */ + target_mem_write(f->t, SRAM_BASE, stm32l4_flash_write_stub, + sizeof(stm32l4_flash_write_stub)); + 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 stm32l4_cmd_erase(target *t, uint32_t action) +{ + const char spinner[] = "|/-\\"; + int spinindex = 0; + + gdb_out("Erasing flash... This may take a few seconds. "); + stm32l4_flash_unlock(t); + + /* Flash erase action start instruction */ + target_mem_write32(t, FLASH_CR, action); + target_mem_write32(t, FLASH_CR, action | FLASH_CR_STRT); + + /* Read FLASH_SR to poll for BSY bit */ + while (target_mem_read32(t, FLASH_SR) & FLASH_SR_BSY) { + gdb_outf("\b%c", spinner[spinindex++ % 4]); + if(target_check_error(t)) { + gdb_out("\n"); + return false; + } + } + gdb_out("\n"); + + /* Check for error */ + uint16_t sr = target_mem_read32(t, FLASH_SR); + if (sr & FLASH_SR_ERROR_MASK) + return false; + return true; +} + +static bool stm32l4_cmd_erase_mass(target *t) +{ + return stm32l4_cmd_erase(t, FLASH_CR_MER1 | FLASH_CR_MER2); +} + +static bool stm32l4_cmd_erase_bank1(target *t) +{ + return stm32l4_cmd_erase(t, FLASH_CR_MER1); +} + +static bool stm32l4_cmd_erase_bank2(target *t) +{ + return stm32l4_cmd_erase(t, FLASH_CR_MER2); +} + +static bool stm32l4_cmd_option(target *t, int argc, char *argv[]) +{ + uint32_t addr, val; + + (void) argc; + (void) argv; + for (int i = 0; i < 0x23; i += 8) { + addr = 0x1fff7800 + i; + val = target_mem_read32(t, addr); + gdb_outf("0x%08X: 0x%08x\n", addr, val); + } + for (int i = 8; i < 0x23; i += 8) { + addr = 0x1ffff800 + i; + val = target_mem_read32(t, addr); + gdb_outf("0x%08X: 0x%08X\n", addr, val); + } + return true; +}