diff --git a/src/Makefile b/src/Makefile index b7faddba31e..af83c8a439c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -44,6 +44,7 @@ SRC = \ kinetis.c \ main.c \ morse.c \ + msp432e4.c \ msp432p4.c \ nrf51.c \ nxpke04.c \ diff --git a/src/target/cortexm.c b/src/target/cortexm.c index dab0b8a758f..4dad84a686a 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -707,6 +707,7 @@ bool cortexm_probe(ADIv5_AP_t *ap) PROBE(lpc43xx_probe); PROBE(kinetis_probe); /* Older K-series */ PROBE(at32fxx_probe); + PROBE(msp432e4_probe); } else if (t->part_id == 0x4cb) { /* Cortex-M23 ROM */ PROBE(gd32f1_probe); /* GD32E23x uses GD32F1 peripherals */ } diff --git a/src/target/msp432e4.c b/src/target/msp432e4.c new file mode 100644 index 00000000000..0c27d088c8b --- /dev/null +++ b/src/target/msp432e4.c @@ -0,0 +1,354 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2022 + * + * 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 - SLAU723a + * MSP423e4xx Technical Reference Manual + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "cortexm.h" + +#define REG_BASE_EEPROM 0x400af000 +#define REG_BASE_FLASH 0x400fd000u +#define REG_BASE_SYSTEM_CONTROL 0x400fe000u + +/* devid 0 + * [31] - 0 - reserved + * [30:28] - 001 - version + * [27:24] - 1000 - reserved + * [23:16] - 0c - device class == msp432e4 + * [15:8] - xx - major + * [7:0] - xx - minor + * devid 1 + * [31:28] - 0001 - version + * [27:24] - 0000 - device family == msp432e4 + * [23:16] - xx - part number w/in family + * [15:13] - bbb - pin count + * [12:8] - 00000 - reserved + * [7:5] - bbb - temperature range + * [4:3] - bb - package type + * [2] - b - rohs + * [1:0] - bb - qualification status + */ +#define REG_DEVID0 (REG_BASE_SYSTEM_CONTROL + 0x0000u) +#define REG_DEVID0_MASK 0xffff0000u +#define REG_DEVID0_MSP432E4 0x180c0000u + +#define REG_DEVID1 (REG_BASE_SYSTEM_CONTROL + 0x0004u) +#define REG_DEVID1_MASK 0xff000000u +#define REG_DEVID1_MSP432E4 0x10000000u + +/* boot config + * [31] - b - lock register + * [30:16] - 7fff - reserved + * [15:13] - x - gpio port + * [12:10] - x - gpio pin + * [9] - b - gpio polarity + * [8] - b - gpio enable + * [7:5] - 111 - reserved + * [4] - b - key select + * [3:2] - 11 - reserved + * [1:0] - bb - debug control + */ +#define REG_BOOTCFG (REG_BASE_SYSTEM_CONTROL + 0x01d0u) + +/* flash peripheral properties + * [31] - 0 - reserved + * [30] - b - prefetch buffer mode + * [29] - b - flash mirror mode + * [28] - b - dma flash access + * [27:23] - 00000 - reserved + * [22:19] - bbbb - eeprom sector size + * [18:16] - bbb - flash sector size + * [15:0] - xxxx - flash size + */ +#define REG_FLASH_PERIPHERAL_PROPERTIES (REG_BASE_FLASH + 0x0fc0u) + +/* sram size + * [31:16] - 0000 - reserved + * [15:0] - xxxx - sram size + */ +#define REG_FLASH_SRAM_SIZE (REG_BASE_FLASH + 0x0fc4u) + +/* control + * [31:16] - xxxx - write key + * [15:4] - 000 - reserved + * [3] - b - commit + * [2] - b - mass erase + * [1] - b - erase sector + * [0] - b - write + * control 2 + * [31:16] - xxxx - write key + * [15:1] - 0000 - reserved + * [0] - b - buffered flash memory write + */ +#define REG_FLASH_FMC (REG_BASE_FLASH + 0x0008u) +#define REG_FLASH_FMC2 (REG_BASE_FLASH + 0x0020u) + +/* raw interrupt status + * [31:14] - 00000 - reserved + * [13] - b - program verify + * [12] - 0 - reserved + * [11] - b - erase verify + * [10] - b - invalid data + * [9] - b - pump voltage + * [8:3] - 00 - reserved + * [2] - b - eeprom status + * [1] - b - program status + * [0] - b - access status + */ +#define REG_FLASH_FCRIS (REG_BASE_FLASH + 0x000cu) + +/* flash write key + * [31:16] - 0000 - reserved + * [15:0] - xxxx - key + */ +#define REG_FLASH_PEKEY (REG_BASE_FLASH + 0x003cu) + +/* flash access address + * [31:20] - 000 - reserved + * [19:0] - xxxxx - operation aligned address + */ +#define REG_FLASH_ADDR (REG_BASE_FLASH + 0x0000u) + +/* flash data */ +#define REG_FLASH_DATA (REG_BASE_FLASH + 0x0004u) + +/* eeprom size + * [31:16] - xxxx - # 16bit words + * [15:0] - xxxx - # 32bit words + */ +#define REG_EEPROM_SIZE (REG_BASE_EEPROM + 0x0000u) + +#define BASE_ADDR_SRAM 0x20000000u +#define BASE_ADDR_FLASH 0x00000000u + +/* Flash operations */ + +/* FixMe - the flash block size might be nice to use, but the part i + * have has a 16k sector which is too big w/ the current build setup. + */ +#define BUFFERED_WRITE_SIZE 0x100 + +/* Erase from addr for len bytes */ +static bool msp432e4_flash_erase_sectors(struct target_flash* f, + target_addr_t addr, size_t len) +{ + target *t = f->t; + uint32_t fmc = + (((target_mem_read32(t, REG_BOOTCFG) & (1 << 4)) + ? 0xa442u + : target_mem_read32(t, REG_FLASH_PEKEY)) << 16) | + (1 << 1); + + for (target_addr_t end = addr + len; addr < end; addr += f->blocksize) { + target_mem_write32(t, REG_FLASH_ADDR, addr); + target_mem_write32(t, REG_FLASH_FMC, fmc); +/* FixMe - verify/timeout bit? */ + while (target_mem_read32(t, REG_FLASH_FMC) & (1 << 1)); + } + return true; +} + +/* special case command for erase all flash */ +static bool msp432e4_flash_erase_all(target* t) +{ + uint32_t fmc = + (((target_mem_read32(t, REG_BOOTCFG) & (1 << 4)) + ? 0xa442u + : target_mem_read32(t, REG_FLASH_PEKEY)) << 16) | + (1 << 2); + target_mem_write32(t, REG_FLASH_FMC, fmc); +/* FixMe - timeout/verify bit? */ + while (target_mem_read32(t, REG_FLASH_FMC) & (1 << 2)); + return true; +} + +/* Program flash */ +static bool msp432e4_flash_write(struct target_flash* f, + target_addr_t dst, + const void* src, size_t len) +{ + target* t = f->t; + + uint32_t fmc = + (( 1 || (target_mem_read32(t, REG_BOOTCFG) & (1 << 4)) + ? 0xa442u + : target_mem_read32(t, REG_FLASH_PEKEY)) << 16) | + (1 << 0); + + /* copy out the aligned part */ + const uint32_t* s = src; + const uint32_t* const e = src + (len / 4); + for (/**/; s < e; dst += sizeof(uint32_t), s++) { + target_mem_write32(t, REG_FLASH_ADDR, dst); + target_mem_write32(t, REG_FLASH_DATA, *s); + target_mem_write32(t, REG_FLASH_FMC, fmc); + while (target_mem_read32(t, REG_FLASH_FMC) & (1 << 0)); + } + + /* endian little hate i */ + if (len & 3) { + const uint8_t* rem = (const uint8_t*)s; + uint32_t val = 0; + switch (len & 3) { + case 3: val |= rem[2] << 16; /* fallthrough */ + case 2: val |= rem[1] << 8; /* fallthrough */ + case 1: val |= rem[0] << 0; /* fallthrough */ + } + + target_mem_write32(t, REG_FLASH_ADDR, dst); + target_mem_write32(t, REG_FLASH_DATA, val); + target_mem_write32(t, REG_FLASH_FMC, fmc); + while (target_mem_read32(t, REG_FLASH_FMC) & (1 << 0)); + } + + return true; +} + +/* Optional commands handlers */ +static bool msp432e4_cmd_erase(target* t, int argc, const char** argv) +{ + if (argc != 2 && argc != 3) + goto err_usage; + + uint32_t addr; + if (strcmp(argv[1], "all") == 0) { + if (argc != 2) + goto err_usage; + return msp432e4_flash_erase_all(t); + } + + char* end; + addr = strtoul(argv[1], &end, 0); + if (end == argv[1]) + goto err_usage; + struct target_flash* f = t->flash; + while (f != NULL) { + if ((f->start <= addr) && (addr < f->start + f->length)) + break; + f = f->next; + } + if (f == NULL) + goto err_usage; + + uint32_t n = 1; + if (argc == 3) { + n = strtoul(argv[2], &end, 0); + if (end == argv[2]) + goto err_usage; + } + n *= f->blocksize; + if (n == 0 || addr + n > f->start + f->length) + goto err_usage; + + return msp432e4_flash_erase_sectors(f, addr, n); + +err_usage: + tc_printf(t, "usage: monitor erase (all | ?)\n"); + return false; +} + +/* Optional commands structure*/ +static const struct command_s msp432e4_cmd_list[] = { + { "erase", msp432e4_cmd_erase, "Erase flash: all | ?" }, + { NULL, NULL, NULL }, +}; + +static void msp432e4_add_flash(target* t, uint32_t sector, uint32_t addr, size_t length) +{ + struct target_flash* f = calloc(1, sizeof(*f)); + if (f == NULL) { + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + + f->start = addr; + f->length = length; + f->blocksize = sector; + f->erase = msp432e4_flash_erase_sectors; + f->write = msp432e4_flash_write; + f->writesize = BUFFERED_WRITE_SIZE; + f->erased = 0xff; + target_add_flash(t, f); +} + +bool msp432e4_probe(target* t) +{ + uint32_t devid0 = target_mem_read32(t, REG_DEVID0); + uint32_t devid1 = target_mem_read32(t, REG_DEVID1); + DEBUG_INFO("%s:%d: devid:%x:%x\n", + __func__, __LINE__, + devid0, devid1); + + /* does it look like an msp432e4 variant? */ + if (((devid0 & REG_DEVID0_MASK) != REG_DEVID0_MSP432E4) || + ((devid1 & REG_DEVID1_MASK) != REG_DEVID1_MSP432E4)) + return false; + DEBUG_INFO("%s:%d: ver:%x:%x part:%x pin:%x temp:%x package:%x\n", + __func__, __LINE__, + (devid0 >> 8) & 0xff, (devid0 >> 0) & 0xff, + (devid1 >> 16) & 0xff, + (devid1 >> 13) & 0x7, + (devid1 >> 5) & 0x7, + (devid1 >> 3) & 0x3); + + /* eeprom size as # of 32bit words but not directly accessible */ + uint32_t size_eeprom = (target_mem_read32(t, REG_EEPROM_SIZE) & 0xffffu) * 4; + + /* sram is banked but interleaved into one logical bank */ + uint32_t size_sram = ((target_mem_read32(t, REG_FLASH_SRAM_SIZE) & 0xffffu) + 1) * 0x100u; + + /* flash is in four banks but two-way interleaved */ + uint32_t flash_props = target_mem_read32(t, REG_FLASH_PERIPHERAL_PROPERTIES); + uint32_t flash_size = ((flash_props & 0xffffu) + 1) * 0x800; + uint32_t flash_sector = (1 << ((flash_props >> 16) & 0x07)) * 1024; + +#define FORMAT "MSP432E4 %"PRIu32"k eeprom / %"PRIu32"k sram / %"PRIu32"k flash" + size_t nb = snprintf(NULL, 0, + FORMAT, + size_eeprom / 1024, size_sram / 1024, flash_size / 1024); + void* p = malloc(nb + 1); + if (p == NULL) + return false; + snprintf(p, nb + 1, + FORMAT, + size_eeprom / 1024, size_sram / 1024, flash_size / 1024); + t->driver = p; + t->target_storage = p; +#undef FORMAT + + target_add_ram(t, BASE_ADDR_SRAM, size_sram); + + /* the flash is physically 4x but is 2x banked and 2x interleaved. */ + msp432e4_add_flash(t, flash_sector, BASE_ADDR_FLASH, flash_size / 2); + msp432e4_add_flash(t, flash_sector, BASE_ADDR_FLASH + flash_size / 2, flash_size / 2); + + /* Connect the optional commands */ + target_add_commands(t, msp432e4_cmd_list, "MSP432E4"); + + /* All done */ + return true; +} diff --git a/src/target/target_probe.h b/src/target/target_probe.h index 456bb7ffcb0..5251cc0b66a 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -60,6 +60,7 @@ bool samd_probe(target *t); bool samx5x_probe(target *t); bool kinetis_probe(target *t); bool efm32_probe(target *t); +bool msp432e4_probe(target *t); bool msp432p4_probe(target *t); bool ke04_probe(target *t); bool rp_probe(target *t);