From ae6f0eadc9748deced4e48074eb7d73064cc4e28 Mon Sep 17 00:00:00 2001 From: newbrain Date: Wed, 6 Jun 2018 22:34:21 +0200 Subject: [PATCH] Support for MSP432 TI MCUs (#353) Introduces flashing and debugging support for Texas Instruments MSP432 series of MCUs --- src/Makefile | 1 + src/target/cortexm.c | 1 + src/target/msp432.c | 374 +++++++++++++++++++++++++++++++++++ src/target/target_internal.h | 1 + 4 files changed, 377 insertions(+) create mode 100644 src/target/msp432.c diff --git a/src/Makefile b/src/Makefile index 6ef91994..797fd15b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -46,6 +46,7 @@ SRC = \ kinetis.c \ main.c \ morse.c \ + msp432.c \ nrf51.c \ platform.c \ sam3x.c \ diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 1687fa92..33344d65 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -314,6 +314,7 @@ bool cortexm_probe(ADIv5_AP_t *ap) PROBE(lmi_probe); PROBE(kinetis_probe); PROBE(efm32_probe); + PROBE(msp432_probe); #undef PROBE return true; diff --git a/src/target/msp432.c b/src/target/msp432.c new file mode 100644 index 00000000..38289fb2 --- /dev/null +++ b/src/target/msp432.c @@ -0,0 +1,374 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2017 newbrain. + * + * 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 MSP432 target specific functions for detecting + * the device, providing the XML memory map and Flash memory programming. + * + * Refereces: + * TI doc - SLAU356G + * MSP423P4xx Technical Reference Manual + * TI doc - SLAS826G + * MSP432P401R, MSP432P401M SimpleLink Mixed-Signal Microcontrollers + * TI doc - SLAA704 + * Flash Operations on MSP432 MCUs + * TI doc - + * MSP432® Peripheral Driver Library User's Guide + * + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "cortexm.h" + +/* TLV: Device info tag, address and expected value */ +#define DEVINFO_TAG_ADDR 0x00201004u +#define DEVINFO_TAG_VALUE 0x0000000Bu + +/* TLV: Device info length, address and expected value */ +#define DEVINFO_LEN_ADDR 0x00201008u +#define DEVINFO_LEN_VALUE 0x00000004u + +/* TLV: Device ID, address and expected values */ +#define DEVID_ADDR 0x0020100Cu +#define DEVID_MSP432P401RIPZ 0x0000A000u +#define DEVID_MSP432P401MIPZ 0x0000A001u +#define DEVID_MSP432P401RIZXH 0x0000A002u +#define DEVID_MSP432P401MIZXH 0x0000A003u +#define DEVID_MSP432P401RIRGC 0x0000A004u +#define DEVID_MSP432P401MIRGC 0x0000A005u + +/* TLV: Hardware revision, address and minimum expected value */ +#define HWREV_ADDR 0x00201010u +#define HWREV_MIN_VALUE 0x00000043u + +/* ROM Device Driver Table pointer addresses */ +#define ROM_APITABLE 0x02000800u + +#define OFS_FLASHCTLTABLE 28 /* ROM_APITABLE[7] */ +#define OFS_FlashCtl_performMassErase 32 /* ROM_FLASHCTLTABLE[8] */ +#define OFS_FlashCtl_eraseSector 36 /* ROM_FLASHCTLTABLE[9] */ +#define OFS_FlashCtl_programMemory 40 /* ROM_FLASHCTLTABLE[10] */ + +/* Memory sizes and base addresses */ +#define MAIN_FLASH_BASE 0x00000000u /* Beginning of Main Flash */ +#define INFO_FLASH_BASE 0x00200000u /* Beginning of Info Flash */ +#define INFO_BANK_SIZE 0x00002000u /* Size of 1 bank of Info Flash */ +#define SECTOR_SIZE 0x1000u /* Size of erase page: 4KB */ + +/* Flash protection registers */ +#define INFO_BANK0_WEPROT 0x400110B0u /* Write/Erase protection Bank 0 Info */ +#define MAIN_BANK0_WEPROT 0x400110B4u /* Write/Erase protection Bank 0 Main */ +#define INFO_BANK1_WEPROT 0x400110C0u /* Write/Erase protection Bank 1 Info */ +#define MAIN_BANK1_WEPROT 0x400110C4u /* Write/Erase protection Bank 1 Main */ + +/* Main Flash and SRAM size registers */ +#define SYS_SRAM_SIZE 0xE0043010u /* Size of SRAM in SYSCTL */ +#define SYS_FLASH_SIZE 0xE0043020u /* Size of main flash in SYSCTL */ + +/* RAM info */ +#define SRAM_BASE 0x20000000u /* Beginning of SRAM */ +#define SRAM_CODE_BASE 0x01000000u /* Beginning of SRAM, Code zone alias */ +#define P401M_SRAM_SIZE 0x00008000u /* Size of SRAM, M: 32KB */ +#define P401R_SRAM_SIZE 0x00010000u /* Size of SRAM, R: 64KB */ + +/* Flash write buffer and stack */ +#define SRAM_STACK_OFFSET 0x00000200u /* A bit less than 512 stack room */ +#define SRAM_STACK_PTR (SRAM_BASE + SRAM_STACK_OFFSET) +#define SRAM_WRITE_BUFFER SRAM_STACK_PTR /* Buffer right above stack */ +#define SRAM_WRITE_BUF_SIZE 0x00000400u /* Write 1024 bytes at a tima */ + +/* Watchdog */ +#define WDT_A_WTDCTL 0x4000480Cu /* Control register for watchdog */ +#define WDT_A_HOLD 0x5A88u /* Clears and halts the watchdog */ + +/* Support variables to call code in ROM */ +struct msp432_flash +{ + struct target_flash f; + target_addr flash_protect_register; /* Address of the WEPROT register*/ + target_addr FlashCtl_eraseSector; /* Erase flash sector routine in ROM*/ + target_addr FlashCtl_programMemory; /* Flash programming routine in ROM */ +}; + +/* Flash operations */ +static bool msp432_sector_erase(struct target_flash *f, target_addr addr); +static int msp432_flash_erase(struct target_flash *f, target_addr addr, size_t len); +static int msp432_flash_write(struct target_flash *f, target_addr dest, + const void *src, size_t len); + +/* Utility functions */ +/* Find the the target flash that conatins a specific address */ +static struct target_flash *get_target_flash(target *t, target_addr addr); + +/* Call a subroutine in the MSP432 ROM (or anywhere else...)*/ +static void msp432_call_ROM(target *t, uint32_t address, uint32_t regs[]); + +/* Protect or unprotect the sector containing address */ +static inline uint32_t msp432_sector_unprotect(struct msp432_flash *mf, target_addr addr) +{ + /* Read the old protection register */ + uint32_t old_mask = target_mem_read32(mf->f.t, mf->flash_protect_register); + /* Find the bit representing the sector and set it to 0 */ + uint32_t sec_mask = ~(1u << ((addr - mf->f.start) / SECTOR_SIZE)); + /* Clear the potection bit */ + sec_mask &= old_mask; + target_mem_write32(mf->f.t, mf->flash_protect_register, sec_mask); + return old_mask; +} + +/* Optional commands handlers */ +/* Erase all of main flash */ +static bool msp432_cmd_erase_main(target *t); +/* Erase a single (4KB) sector */ +static bool msp432_cmd_sector_erase(target *t, int argc, char *argv[]); + +/* Optional commands structure*/ +const struct command_s msp432_cmd_list[] = { + {"erase", (cmd_handler)msp432_cmd_erase_main, "Erase main flash"}, + {"sector_erase", (cmd_handler)msp432_cmd_sector_erase, "Erase sector containing given address"}, + {NULL, NULL, NULL}}; + +static void msp432_add_flash(target *t, uint32_t addr, size_t length, target_addr prot_reg) +{ + struct msp432_flash *mf = calloc(1, sizeof(*mf)); + struct target_flash *f = &mf->f; + f->start = addr; + f->length = length; + f->blocksize = SECTOR_SIZE; + f->erase = msp432_flash_erase; + f->write = msp432_flash_write; + f->buf_size = SRAM_WRITE_BUF_SIZE; + f->erased = 0xff; + target_add_flash(t, f); + /* Initialize ROM call pointers. Silicon rev B is not supported */ + uint32_t flashctltable = + target_mem_read32(t, ROM_APITABLE + OFS_FLASHCTLTABLE); + mf->FlashCtl_eraseSector = + target_mem_read32(t, flashctltable + OFS_FlashCtl_eraseSector); + mf->FlashCtl_programMemory = + target_mem_read32(t, flashctltable + OFS_FlashCtl_programMemory); + mf->flash_protect_register = prot_reg; +} + +bool msp432_probe(target *t) +{ + /* Check for the right device info tag in the TLV ROM structure */ + if (target_mem_read32(t, DEVINFO_TAG_ADDR) != DEVINFO_TAG_VALUE) + return false; + + /* Check for the right device info length tag in the TLV ROM structure */ + if (target_mem_read32(t, DEVINFO_LEN_ADDR) != DEVINFO_LEN_VALUE) + return false; + + /* Check for the right HW revision: at least C, as no flash support for B */ + if (target_mem_read32(t, HWREV_ADDR) < HWREV_MIN_VALUE) + return false; + + /* If we got till this point, we are most probably looking at a real TLV */ + /* Device Information structure. Now check for the correct device */ + switch (target_mem_read32(t, DEVID_ADDR)) { + case DEVID_MSP432P401RIPZ: + case DEVID_MSP432P401RIZXH: + case DEVID_MSP432P401RIRGC: + /* R series: 256kB Flash, 64kB RAM */ + t->driver = "MSP432P401R 256KB Flash 64KB RAM"; + break; + + case DEVID_MSP432P401MIPZ: + case DEVID_MSP432P401MIZXH: + case DEVID_MSP432P401MIRGC: + /* M series: 128kB Flash, 32kB RAM */ + t->driver = "MSP432P401M 128KB Flash 32KB RAM"; + break; + + default: + /* Unknown device, not an MSP432 or not a real TLV */ + return false; + } + /* SRAM region, SRAM zone */ + target_add_ram(t, SRAM_BASE, target_mem_read32(t, SYS_SRAM_SIZE)); + /* Flash bank size */ + uint32_t banksize = target_mem_read32(t, SYS_FLASH_SIZE) / 2; + /* Main Flash Bank 0 */ + msp432_add_flash(t, MAIN_FLASH_BASE, banksize, MAIN_BANK0_WEPROT); + /* Main Flash Bank 1 */ + msp432_add_flash(t, MAIN_FLASH_BASE + banksize, banksize, MAIN_BANK1_WEPROT); + /* Info Flash Bank 0 */ + msp432_add_flash(t, INFO_FLASH_BASE, INFO_BANK_SIZE, INFO_BANK0_WEPROT); + /* Info Flash Bank 1 */ + msp432_add_flash(t, INFO_FLASH_BASE + INFO_BANK_SIZE, INFO_BANK_SIZE, INFO_BANK1_WEPROT); + + /* Connect the optional commands */ + target_add_commands(t, msp432_cmd_list, "MSP432P401x"); + + /* All done */ + return true; +} + +/* Flash operations */ +/* Erase a single sector at addr calling the ROM routine*/ +static bool msp432_sector_erase(struct target_flash *f, target_addr addr) +{ + target *t = f->t; + struct msp432_flash *mf = (struct msp432_flash *)f; + + /* Unprotect sector */ + uint32_t old_prot = msp432_sector_unprotect(mf, addr); + DEBUG("Flash protect: 0x%08"PRIX32"\n", target_mem_read32(t, mf->flash_protect_register)); + + /* Prepare input data */ + uint32_t regs[t->regs_size / sizeof(uint32_t)]; // Use of VLA + target_regs_read(t, regs); + regs[0] = addr; // Address of sector to erase in R0 + + DEBUG("Erasing sector at 0x%08"PRIX32"\n", addr); + + /* Call ROM */ + msp432_call_ROM(t, mf->FlashCtl_eraseSector, regs); + + // Result value in R0 is true for success + DEBUG("ROM return value: %"PRIu32"\n", regs[0]); + + /* Restore original protection */ + target_mem_write32(t, mf->flash_protect_register, old_prot); + + return !regs[0]; +} + +/* Erase from addr for len bytes */ +static int msp432_flash_erase(struct target_flash *f, target_addr addr, size_t len) +{ + int ret = 0; + while (len) { + ret |= msp432_sector_erase(f, addr); + + /* update len and addr */ + len -= f->blocksize; + addr += f->blocksize; + } + + return ret; +} + +/* Program flash */ +static int msp432_flash_write(struct target_flash *f, target_addr dest, + const void *src, size_t len) +{ + struct msp432_flash *mf = (struct msp432_flash *)f; + target *t = f->t; + + /* Prepare RAM buffer in target */ + target_mem_write(t, SRAM_WRITE_BUFFER, src, len); + + /* Unprotect sector, len is always < SECTOR_SIZE */ + uint32_t old_prot = msp432_sector_unprotect(mf, dest); + + DEBUG("Flash protect: 0x%08"PRIX32"\n", target_mem_read32(t, mf->flash_protect_register)); + + /* Prepare input data */ + uint32_t regs[t->regs_size / sizeof(uint32_t)]; // Use of VLA + target_regs_read(t, regs); + regs[0] = SRAM_WRITE_BUFFER; // Address of buffer to be flashed in R0 + regs[1] = dest; // Flash address to be write to in R1 + regs[2] = len; // Size of buffer to be flashed in R2 + + DEBUG("Writing 0x%04"PRIX32" bytes at 0x%08zX\n", dest, len); + /* Call ROM */ + msp432_call_ROM(t, mf->FlashCtl_programMemory, regs); + + /* Restore original protection */ + target_mem_write32(t, mf->flash_protect_register, old_prot); + + DEBUG("ROM return value: %"PRIu32"\n", regs[0]); + // Result value in R0 is true for success + return !regs[0]; +} + +/* Optional commands handlers */ +static bool msp432_cmd_erase_main(target *t) +{ + /* The mass erase routine in ROM will also erase the Info Flash. */ + /* Usually, this is not wanted, so go sector by sector... */ + + uint32_t banksize = target_mem_read32(t, SYS_FLASH_SIZE) / 2; + DEBUG("Bank Size: 0x%08"PRIX32"\n", banksize); + + /* Erase first bank */ + struct target_flash *f = get_target_flash(t, MAIN_FLASH_BASE); + bool ret = msp432_flash_erase(f, MAIN_FLASH_BASE, banksize); + + /* Erase second bank */ + f = get_target_flash(t, MAIN_FLASH_BASE + banksize); + ret |= msp432_flash_erase(f, MAIN_FLASH_BASE + banksize, banksize); + + return ret; +} + +static bool msp432_cmd_sector_erase(target *t, int argc, char *argv[]) +{ + if (argc < 2) + tc_printf(t, "usage: monitor sector_erase \n"); + + uint32_t addr = strtoul(argv[1], NULL, 0); + + /* Find the flash structure (for the rigth protect register) */ + struct target_flash *f = get_target_flash(t, addr); + + if (f) + return msp432_sector_erase(f, addr); + tc_printf(t, "Invalid sector address\n"); + return false; +} + +/* Returns flash bank containing addr, or NULL if not found */ +static struct target_flash *get_target_flash(target *t, target_addr addr) +{ + struct target_flash *f = t->flash; + while (f) { + if ((f->start <= addr) && (addr < f->start + f->length)) + break; + f = f->next; + } + return f; +} + +/* MSP432 ROM routine invocation */ +static void msp432_call_ROM(target *t, uint32_t address, uint32_t regs[]) +{ + /* Kill watchdog */ + target_mem_write16(t, WDT_A_WTDCTL, WDT_A_HOLD); + + /* Breakpoint at the beginning of CODE SRAM alias area */ + target_mem_write16(t, SRAM_CODE_BASE, ARM_THUMB_BREAKPOINT); + + /* Prepare registers */ + regs[REG_MSP] = SRAM_STACK_PTR; /* Stack space */ + regs[REG_LR] = SRAM_CODE_BASE | 1; /* Return to beginning of SRAM CODE alias */ + regs[REG_PC] = address; /* Start at given address */ + target_regs_write(t, regs); + + /* Call ROM */ + /* start the target and wait for it to halt again */ + target_halt_resume(t, false); + while (!target_halt_poll(t, NULL)); + + // Read registers to get result + target_regs_read(t, regs); +} \ No newline at end of file diff --git a/src/target/target_internal.h b/src/target/target_internal.h index caa542af..d78ed537 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -175,5 +175,6 @@ bool nrf51_probe(target *t); bool samd_probe(target *t); bool kinetis_probe(target *t); bool efm32_probe(target *t); +bool msp432_probe(target *t); #endif