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