diff --git a/src/Makefile b/src/Makefile index 797fd15..d416ddb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -54,6 +54,7 @@ SRC = \ samd.c \ stm32f1.c \ stm32f4.c \ + stm32h7.c \ stm32l0.c \ stm32l4.c \ swdptap.c \ diff --git a/src/target/cortexm.c b/src/target/cortexm.c index cbd769b..4f6de80 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -330,6 +330,7 @@ bool cortexm_probe(ADIv5_AP_t *ap) PROBE(stm32f1_probe); PROBE(stm32f4_probe); + PROBE(stm32h7_probe); PROBE(stm32l0_probe); /* STM32L0xx & STM32L1xx */ PROBE(stm32l4_probe); PROBE(lpc11xx_probe); diff --git a/src/target/stm32h7.c b/src/target/stm32h7.c new file mode 100644 index 0000000..5e84376 --- /dev/null +++ b/src/target/stm32h7.c @@ -0,0 +1,486 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2017-2018 Uwe Bonnes bon@elektron.ikp.physik.tu-darmstadt.de + * + * 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 STM32H7 target specific functions for detecting + * the device, providing the XML memory map and Flash memory programming. + * + * Refereces: + * ST doc - RM0433 + * Reference manual - STM32H7x3 advanced ARMĀ®-based 32-bit MCUs Rev.3 + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "cortexm.h" + +static bool stm32h7_cmd_erase_mass(target *t); +/* static bool stm32h7_cmd_option(target *t, int argc, char *argv[]); */ +static bool stm32h7_uid(target *t); +static bool stm32h7_crc(target *t); +static bool stm32h7_cmd_psize(target *t, int argc, char *argv[]); + +const struct command_s stm32h7_cmd_list[] = { + {"erase_mass", (cmd_handler)stm32h7_cmd_erase_mass, + "Erase entire flash memory"}, +/* {"option", (cmd_handler)stm32h7_cmd_option, + "Manipulate option bytes"},*/ + {"psize", (cmd_handler)stm32h7_cmd_psize, + "Configure flash write parallelism: (x8|x16|x32|x64(default))"}, + {"uid", (cmd_handler)stm32h7_uid, "Print unique device ID"}, + {"crc", (cmd_handler)stm32h7_crc, "Print CRC of both banks"}, + {NULL, NULL, NULL} +}; + + +static int stm32h7_flash_erase(struct target_flash *f, target_addr addr, + size_t len); +static int stm32h7_flash_write(struct target_flash *f, + target_addr dest, const void *src, size_t len); + +static const char stm32h74_driver_str[] = "STM32H74x"; + +enum stm32h7_regs +{ + FLASH_ACR = 0x00, + FLASH_KEYR = 0x04, + FLASH_OPTKEYR = 0x08, + FLASH_CR = 0x0c, + FLASH_SR = 0x10, + FLASH_CCR = 0x14, + FLASH_OPTCR = 0x18, + FLASH_OPTSR_CUR = 0x1C, + FLASH_OPTSR = 0x20, + FLASH_CRCCR = 0x50, + FLASH_CRCDATA = 0x5C, +}; + +/* Flash Program and Erase Controller Register Map */ +#define H7_IWDG_BASE 0x58004c00 +#define FPEC1_BASE 0x52002000 +#define FPEC2_BASE 0x52002100 +#define FLASH_SR_BSY (1 << 0) +#define FLASH_SR_WBNE (1 << 1) +#define FLASH_SR_QW (1 << 2) +#define FLASH_SR_CRC_BUSY (1 << 3) +#define FLASH_SR_EOP (1 << 16) +#define FLASH_SR_WRPERR (1 << 17) +#define FLASH_SR_PGSERR (1 << 18) +#define FLASH_SR_STRBERR (1 << 19) +#define FLASH_SR_INCERR (1 << 21) +#define FLASH_SR_OPERR (1 << 22) +#define FLASH_SR_OPERR (1 << 22) +#define FLASH_SR_RDPERR (1 << 23) +#define FLASH_SR_RDSERR (1 << 24) +#define FLASH_SR_SNECCERR (1 << 25) +#define FLASH_SR_DBERRERR (1 << 26) +#define FLASH_SR_ERROR_READ (FLASH_SR_RDPERR | FLASH_SR_RDSERR | \ + FLASH_SR_SNECCERR |FLASH_SR_DBERRERR) +#define FLASH_SR_ERROR_MASK ( \ + FLASH_SR_WRPERR | FLASH_SR_PGSERR | FLASH_SR_STRBERR | \ + FLASH_SR_INCERR | FLASH_SR_OPERR | FLASH_SR_ERROR_READ) +#define FLASH_CR_LOCK (1 << 0) +#define FLASH_CR_PG (1 << 1) +#define FLASH_CR_SER (1 << 2) +#define FLASH_CR_BER (1 << 3) +#define FLASH_CR_PSIZE8 (0 << 4) +#define FLASH_CR_PSIZE16 (1 << 4) +#define FLASH_CR_PSIZE32 (2 << 4) +#define FLASH_CR_PSIZE64 (3 << 4) +#define FLASH_CR_FW (1 << 6) +#define FLASH_CR_START (1 << 7) +#define FLASH_CR_SNB_1 (1 << 8) +#define FLASH_CR_SNB (3 << 8) +#define FLASH_CR_CRC_EN (1 << 15) + +#define FLASH_OPTCR_OPTLOCK (1 << 0) +#define FLASH_OPTCR_OPTSTRT (1 << 1) + +#define FLASH_OPTSR_IWDG1_SW (1 << 4) + +#define FLASH_CRCCR_ALL_BANK (1 << 7) +#define FLASH_CRCCR_START_CRC (1 << 16) +#define FLASH_CRCCR_CLEAN_CRC (1 << 17) +#define FLASH_CRCCR_CRC_BURST_3 (3 << 20) + +#define KEY1 0x45670123 +#define KEY2 0xCDEF89AB + +#define OPTKEY1 0x08192A3B +#define OPTKEY2 0x4C5D6E7F + +#define DBGMCU_IDCODE 0x5c001000 +/* Access via 0xe00e1000 does not show device! */ +#define DBGMCU_CR (DBGMCU_IDCODE + 4) +#define DBGSLEEP_D1 (1 << 0) +#define DBGSTOP_D1 (1 << 1) +#define DBGSTBY_D1 (1 << 2) +#define DBGSTOP_D3 (1 << 7) +#define DBGSTBY_D3 (1 << 8) +#define D1DBGCKEN (1 << 21) +#define D3DBGCKEN (1 << 22) + + +#define FLASH_SIZE_REG 0x1ff1e880 + +#define BANK1_START 0x08000000 +#define NUM_SECTOR_PER_BANK 8 +#define FLASH_SECTOR_SIZE 0x20000 +#define BANK2_START 0x08100000 +enum ID_STM32H7 { + ID_STM32H74x = 0x450, +}; + +struct stm32h7_flash { + struct target_flash f; + enum align psize; + uint32_t regbase; +}; + +static void stm32h7_add_flash(target *t, + uint32_t addr, size_t length, size_t blocksize) +{ + struct stm32h7_flash *sf = calloc(1, sizeof(*sf)); + struct target_flash *f = &sf->f; + f->start = addr; + f->length = length; + f->blocksize = blocksize; + f->erase = stm32h7_flash_erase; + f->write = stm32h7_flash_write; + f->buf_size = 2048; + f->erased = 0xff; + sf->regbase = FPEC1_BASE; + if (addr >= BANK2_START) + sf->regbase = FPEC2_BASE; + sf->psize = ALIGN_DWORD; + target_add_flash(t, f); +} + +bool stm32h7_probe(target *t) +{ + uint32_t idcode = target_mem_read32(t, DBGMCU_IDCODE) & 0xFFF; + if (idcode == ID_STM32H74x) { + /* RM0433 Rev 4 is not really clear, what bits are needed. + * Set all possible relevant bits for now. */ + target_mem_write32(t, DBGMCU_CR, DBGSLEEP_D1 | D1DBGCKEN); + t->idcode = idcode; + t->driver = stm32h74_driver_str; + target_add_commands(t, stm32h7_cmd_list, stm32h74_driver_str); + target_add_ram(t, 0x00000000, 0x10000); /* ITCM Ram, 64 k */ + target_add_ram(t, 0x20000000, 0x20000); /* DTCM Ram, 128 k */ + target_add_ram(t, 0x24000000, 0x80000); /* AXI Ram, 512 k */ + target_add_ram(t, 0x30000000, 0x20000); /* AHB SRAM1, 128 k */ + target_add_ram(t, 0x32000000, 0x20000); /* AHB SRAM2, 128 k */ + target_add_ram(t, 0x34000000, 0x08000); /* AHB SRAM3, 32 k */ + target_add_ram(t, 0x38000000, 0x01000); /* AHB SRAM4, 32 k */ + stm32h7_add_flash(t, 0x8000000, 0x100000, FLASH_SECTOR_SIZE); + stm32h7_add_flash(t, 0x8100000, 0x100000, FLASH_SECTOR_SIZE); + /* If IWDG runs as HARDWARE watchdog (44.3.4) erase + * will be aborted by the Watchdog and erase fails! + * Setting IWDG_KR to 0xaaaa does not seem to help!*/ + uint32_t optsr = target_mem_read32(t, FPEC1_BASE + FLASH_OPTSR); + if (!(optsr & FLASH_OPTSR_IWDG1_SW)) + tc_printf(t, "Hardware IWDG running. Expect failure. Set IWDG1_SW!"); + return true; + } + return false; +} + +static bool stm32h7_flash_unlock(target *t, uint32_t addr) +{ + uint32_t regbase = FPEC1_BASE; + if (addr >= BANK2_START) { + regbase = FPEC2_BASE; + } + + while(target_mem_read32(t, regbase + FLASH_SR) & FLASH_SR_BSY) { + if(target_check_error(t)) + return false; + } + uint32_t sr = target_mem_read32(t, regbase + FLASH_SR); + if (sr & FLASH_SR_ERROR_MASK) { + tc_printf(t, "Error 0x%08lx", sr & FLASH_SR_ERROR_MASK); + target_mem_write32(t, regbase + FLASH_CCR, sr & FLASH_SR_ERROR_MASK); + return false; + } + if (target_mem_read32(t, regbase + FLASH_CR) & FLASH_CR_LOCK) { + /* Enable FLASH controller access */ + target_mem_write32(t, regbase + FLASH_KEYR, KEY1); + target_mem_write32(t, regbase + FLASH_KEYR, KEY2); + } + if (target_mem_read32(t, regbase + FLASH_CR) & FLASH_CR_LOCK) + return false; + else + return true; +} + +static int stm32h7_flash_erase(struct target_flash *f, target_addr addr, + size_t len) +{ + target *t = f->t; + struct stm32h7_flash *sf = (struct stm32h7_flash *)f; + if (stm32h7_flash_unlock(t, addr) == false) + return -1; + /* We come out of reset with HSI 64 MHz. Adapt FLASH_ACR.*/ + target_mem_write32(t, sf->regbase + FLASH_ACR, 0); + addr &= (NUM_SECTOR_PER_BANK * FLASH_SECTOR_SIZE) - 1; + int start_sector = addr / FLASH_SECTOR_SIZE; + int end_sector = (addr + len - 1) / FLASH_SECTOR_SIZE; + + enum align psize = ((struct stm32h7_flash *)f)->psize; + uint32_t sr; + while (start_sector <= end_sector) { + uint32_t cr = (psize * FLASH_CR_PSIZE16) | FLASH_CR_SER | + (start_sector * FLASH_CR_SNB_1); + target_mem_write32(t, sf->regbase + FLASH_CR, cr); + cr |= FLASH_CR_START; + target_mem_write32(t, sf->regbase + FLASH_CR, cr); + DEBUG(" started cr %08" PRIx32 " sr %08" PRIx32 "\n", + target_mem_read32(t, sf->regbase + FLASH_CR), + target_mem_read32(t, sf->regbase + FLASH_SR)); + do { + sr = target_mem_read32(t, sf->regbase + FLASH_SR); + if (target_check_error(t)) { + DEBUG("stm32h7_flash_erase: comm failed\n"); + return -1; + } +// target_mem_write32(t, H7_IWDG_BASE, 0x0000aaaa); + }while (sr & (FLASH_SR_QW | FLASH_SR_BSY)); + if (sr & FLASH_SR_ERROR_MASK) { + DEBUG("stm32h7_flash_erase: error, sr: %08" PRIx32 "\n", sr); + return -1; + } + start_sector++; + } + return 0; +} + +static int stm32h7_flash_write(struct target_flash *f, target_addr dest, + const void *src, size_t len) +{ + target *t = f->t; + struct stm32h7_flash *sf = (struct stm32h7_flash *)f; + enum align psize = sf->psize; + if (stm32h7_flash_unlock(t, dest) == false) + return -1; + uint32_t cr = psize * FLASH_CR_PSIZE16; + target_mem_write32(t, sf->regbase + FLASH_CR, cr); + cr |= FLASH_CR_PG; + target_mem_write32(t, sf->regbase + FLASH_CR, cr); + /* does H7 stall?*/ + uint32_t sr_reg = sf->regbase + FLASH_SR; + uint32_t sr; + target_mem_write(t, dest, src, len); + while ((sr = target_mem_read32(t, sr_reg)) & FLASH_SR_BSY) { + if(target_check_error(t)) { + DEBUG("stm32h7_flash_write: BSY comm failed\n"); + return -1; + } + } + if (sr & FLASH_SR_ERROR_MASK) { + DEBUG("stm32h7_flash_write: error sr %08" PRIx32 "\n", sr); + return -1; + } + /* Close write windows.*/ + target_mem_write32(t, sf->regbase + FLASH_CR, 0); + return 0; +} + +/* Both banks are erased in parallel.*/ +static bool stm32h7_cmd_erase(target *t, int bank_mask) +{ + const char spinner[] = "|/-\\"; + int spinindex = 0; + bool do_bank1 = bank_mask & 1, do_bank2 = bank_mask & 2; + uint32_t cr; + bool result = false; + enum align psize = ALIGN_DWORD; + for (struct target_flash *f = t->flash; f; f = f->next) { + if (f->write == stm32h7_flash_write) { + psize = ((struct stm32h7_flash *)f)->psize; + } + } + cr = (psize * FLASH_CR_PSIZE16) | FLASH_CR_BER | FLASH_CR_START; + /* Flash mass erase start instruction */ + if (do_bank1) { + if (stm32h7_flash_unlock(t, BANK1_START) == false) { + DEBUG("ME: Unlock bank1 failed\n"); + goto done; + } + uint32_t regbase = FPEC1_BASE; + /* BER and start can be merged (3.3.10).*/ + target_mem_write32(t, regbase + FLASH_CR, cr); + DEBUG("ME bank1 started\n"); + } + if (do_bank2) { + if (stm32h7_flash_unlock(t, BANK2_START) == false) { + DEBUG("ME: Unlock bank2 failed\n"); + goto done; + } + uint32_t regbase = FPEC2_BASE; + /* BER and start can be merged (3.3.10).*/ + target_mem_write32(t, regbase + FLASH_CR, cr); + DEBUG("ME bank2 started\n"); + } + + /* Read FLASH_SR to poll for QW bit */ + if (do_bank1) { + uint32_t regbase = FPEC1_BASE; + while (target_mem_read32(t, regbase + FLASH_SR) & FLASH_SR_QW) { +// target_mem_write32(t, H7_IWDG_BASE, 0x0000aaaa); + tc_printf(t, "\b%c", spinner[spinindex++ % 4]); + if(target_check_error(t)) { + DEBUG("ME bank1: comm failed\n"); + goto done; + } + } + } + if (do_bank2) { + uint32_t regbase = FPEC2_BASE; + while (target_mem_read32(t, regbase + FLASH_SR) & FLASH_SR_QW) { +// target_mem_write32(t, H7_IWDG_BASE 0x0000aaaa); + tc_printf(t, "\b%c", spinner[spinindex++ % 4]); + if(target_check_error(t)) { + DEBUG("ME bank2: comm failed\n"); + goto done; + } + } + } + + if (do_bank1) { + /* Check for error */ + uint32_t regbase = FPEC1_BASE; + uint32_t sr = target_mem_read32(t, regbase + FLASH_SR); + if (sr & FLASH_SR_ERROR_MASK) { + DEBUG("ME bank1: sr %" PRIx32 "\n", sr); + goto done; + } + } + if (do_bank2) { + /* Check for error */ + uint32_t regbase = FPEC2_BASE; + uint32_t sr = target_mem_read32(t, regbase + FLASH_SR); + if (sr & FLASH_SR_ERROR_MASK) { + DEBUG("ME bank2: sr %" PRIx32 "\n", sr); + goto done; + } + } + result = true; + done: + tc_printf(t, "\n"); + return result; +} + +static bool stm32h7_cmd_erase_mass(target *t) +{ + tc_printf(t, "Erasing flash... This may take a few seconds. "); + return stm32h7_cmd_erase(t, 3); +} + +/* Print the Unique device ID. + * Can be reused for other STM32 devices With uid as parameter. + */ +static bool stm32h7_uid(target *t) +{ + uint32_t uid = 0x1ff1e800; + int i; + tc_printf(t, "0x"); + for (i = 0; i < 12; i = i + 4) { + uint32_t val = target_mem_read32(t, uid + i); + tc_printf(t, "%02X", (val >> 24) & 0xff); + tc_printf(t, "%02X", (val >> 16) & 0xff); + tc_printf(t, "%02X", (val >> 8) & 0xff); + tc_printf(t, "%02X", (val >> 0) & 0xff); + } + tc_printf(t, "\n"); + return true; +} +static int stm32h7_crc_bank(target *t, uint32_t bank) +{ + uint32_t regbase = FPEC1_BASE; + if (bank >= BANK2_START) + regbase = FPEC2_BASE; + + if (stm32h7_flash_unlock(t, bank) == false) + return -1; + uint32_t cr = FLASH_CR_CRC_EN; + target_mem_write32(t, regbase + FLASH_CR, cr); + uint32_t crccr= FLASH_CRCCR_CRC_BURST_3 | + FLASH_CRCCR_CLEAN_CRC | FLASH_CRCCR_ALL_BANK; + target_mem_write32(t, regbase + FLASH_CRCCR, crccr); + target_mem_write32(t, regbase + FLASH_CRCCR, crccr | FLASH_CRCCR_START_CRC); + uint32_t sr; + while ((sr = target_mem_read32(t, regbase + FLASH_SR)) & FLASH_SR_CRC_BUSY) { + if(target_check_error(t)) { + DEBUG("CRC bank %d: comm failed\n", (bank < BANK2_START) ? 1 : 2); + return -1; + } + if (sr & FLASH_SR_ERROR_READ) { + DEBUG("CRC bank %d: error sr %08" PRIx32 "\n", + (bank < BANK2_START) ? 1 : 2, sr); + return -1; + } + } + return 0; +} + +static bool stm32h7_crc(target *t) +{ + if (stm32h7_crc_bank(t, BANK1_START) ) return false; + uint32_t crc1 = target_mem_read32(t, FPEC1_BASE + FLASH_CRCDATA); + if (stm32h7_crc_bank(t, BANK2_START) ) return false; + uint32_t crc2 = target_mem_read32(t, FPEC1_BASE + FLASH_CRCDATA); + tc_printf(t, "CRC: bank1 0x%08lx, bank2 0x%08lx\n", crc1, crc2); + return true; +} +static bool stm32h7_cmd_psize(target *t, int argc, char *argv[]) +{ + if (argc == 1) { + enum align psize = ALIGN_DWORD; + for (struct target_flash *f = t->flash; f; f = f->next) { + if (f->write == stm32h7_flash_write) { + psize = ((struct stm32h7_flash *)f)->psize; + } + } + tc_printf(t, "Flash write parallelism: %s\n", + psize == ALIGN_DWORD ? "x64" : + psize == ALIGN_WORD ? "x32" : + psize == ALIGN_HALFWORD ? "x16" : "x8"); + } else { + enum align psize; + if (!strcmp(argv[1], "x8")) { + psize = ALIGN_BYTE; + } else if (!strcmp(argv[1], "x16")) { + psize = ALIGN_HALFWORD; + } else if (!strcmp(argv[1], "x32")) { + psize = ALIGN_WORD; + } else if (!strcmp(argv[1], "x64")) { + psize = ALIGN_DWORD; + } else { + tc_printf(t, "usage: monitor psize (x8|x16|x32|x64)\n"); + return false; + } + for (struct target_flash *f = t->flash; f; f = f->next) { + if (f->write == stm32h7_flash_write) { + ((struct stm32h7_flash *)f)->psize = psize; + } + } + } + return true; +} diff --git a/src/target/target_internal.h b/src/target/target_internal.h index d78ed53..62664a8 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -161,6 +161,7 @@ int tc_system(target *t, target_addr cmd, size_t cmdlen); */ bool stm32f1_probe(target *t); bool stm32f4_probe(target *t); +bool stm32h7_probe(target *t); bool stm32l0_probe(target *t); bool stm32l1_probe(target *t); bool stm32l4_probe(target *t);