#include "general.h"
#include "target.h"
#include "target_internal.h"
#include "cortex.h"
#define DEVINFO_TAG_ADDR 0x00201004U
#define DEVINFO_TAG_VALUE 0x0000000bU
#define DEVINFO_LEN_ADDR 0x00201008U
#define DEVINFO_LEN_VALUE 0x00000004U
#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
#define HWREV_ADDR 0x00201010U
#define HWREV_MIN_VALUE 0x00000043U
#define ROM_TABLE_BASE 0x02000800U
#define OFFS_FLASH_CTRL_TABLE 28U
#define OFFS_FLASH_CTRL_MASS_ERASE 32U
#define OFFS_FLASH_CTRL_ERASE_SECTOR 36U
#define OFFS_FLASH_CTRL_PROGRAM_MEM 40U
#define MAIN_FLASH_BASE 0x00000000U
#define INFO_FLASH_BASE 0x00200000U
#define INFO_BANK_SIZE 0x00002000U
#define SECTOR_SIZE 0x1000U
#define INFO_BANK0_WEPROT 0x400110b0U
#define MAIN_BANK0_WEPROT 0x400110b4U
#define INFO_BANK1_WEPROT 0x400110c0U
#define MAIN_BANK1_WEPROT 0x400110c4U
#define SYS_SRAM_SIZE 0xe0043010U
#define SYS_FLASH_SIZE 0xe0043020U
#define SRAM_BASE 0x20000000U
#define SRAM_CODE_BASE 0x01000000U
#define P401M_SRAM_SIZE 0x00008000U
#define P401R_SRAM_SIZE 0x00010000U
#define SRAM_STACK_OFFSET 0x00000200U
#define SRAM_STACK_PTR (SRAM_BASE + SRAM_STACK_OFFSET)
#define SRAM_WRITE_BUFFER SRAM_STACK_PTR
#define SRAM_WRITE_BUF_SIZE 0x00000400U
#define WDT_A_WTDCTL 0x4000480cU
#define WDT_A_HOLD 0x5a88U
typedef struct msp432_flash {
target_flash_s f;
target_addr_t flash_protect_register;
target_addr_t flash_erase_sector_fn;
target_addr_t flash_program_fn;
} msp432_flash_s;
static bool msp432_sector_erase(target_flash_s *f, target_addr_t addr);
static bool msp432_flash_erase(target_flash_s *f, target_addr_t addr, size_t len);
static bool msp432_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len);
static void msp432_call_rom(target_s *t, uint32_t address, uint32_t *regs);
static inline uint32_t msp432_sector_unprotect(msp432_flash_s *mf, target_addr_t addr)
{
uint32_t old_mask = target_mem_read32(mf->f.t, mf->flash_protect_register);
uint32_t sec_mask = ~(1U << ((addr - mf->f.start) / SECTOR_SIZE));
sec_mask &= old_mask;
target_mem_write32(mf->f.t, mf->flash_protect_register, sec_mask);
return old_mask;
}
static bool msp432_cmd_erase_main(target_s *t, int argc, const char **argv);
static bool msp432_cmd_sector_erase(target_s *t, int argc, const char **argv);
const command_s msp432_cmd_list[] = {
{"erase", msp432_cmd_erase_main, "Erase main flash"},
{"sector_erase", msp432_cmd_sector_erase, "Erase sector containing given address"},
{NULL, NULL, NULL},
};
static void msp432_add_flash(target_s *t, uint32_t addr, size_t length, target_addr_t prot_reg)
{
msp432_flash_s *mf = calloc(1, sizeof(*mf));
target_flash_s *f;
if (!mf) {
DEBUG_ERROR("calloc: failed in %s\n", __func__);
return;
}
f = &mf->f;
f->start = addr;
f->length = length;
f->blocksize = SECTOR_SIZE;
f->erase = msp432_flash_erase;
f->write = msp432_flash_write;
f->writesize = SRAM_WRITE_BUF_SIZE;
f->erased = 0xff;
target_add_flash(t, f);
const uint32_t flash_ctrl_base = target_mem_read32(t, ROM_TABLE_BASE + OFFS_FLASH_CTRL_TABLE);
mf->flash_erase_sector_fn = target_mem_read32(t, flash_ctrl_base + OFFS_FLASH_CTRL_ERASE_SECTOR);
mf->flash_program_fn = target_mem_read32(t, flash_ctrl_base + OFFS_FLASH_CTRL_PROGRAM_MEM);
mf->flash_protect_register = prot_reg;
}
bool msp432p4_probe(target_s *t)
{
if (target_mem_read32(t, DEVINFO_TAG_ADDR) != DEVINFO_TAG_VALUE)
return false;
if (target_mem_read32(t, DEVINFO_LEN_ADDR) != DEVINFO_LEN_VALUE)
return false;
if (target_mem_read32(t, HWREV_ADDR) < HWREV_MIN_VALUE) {
DEBUG_INFO("MSP432 Version not handled\n");
return false;
}
switch (target_mem_read32(t, DEVID_ADDR)) {
case DEVID_MSP432P401RIPZ:
case DEVID_MSP432P401RIZXH:
case DEVID_MSP432P401RIRGC:
t->driver = "MSP432P401R 256KB Flash 64KB RAM";
break;
case DEVID_MSP432P401MIPZ:
case DEVID_MSP432P401MIZXH:
case DEVID_MSP432P401MIRGC:
t->driver = "MSP432P401M 128KB Flash 32KB RAM";
break;
default:
return false;
}
target_add_ram(t, SRAM_BASE, target_mem_read32(t, SYS_SRAM_SIZE));
uint32_t banksize = target_mem_read32(t, SYS_FLASH_SIZE) / 2U;
msp432_add_flash(t, MAIN_FLASH_BASE, banksize, MAIN_BANK0_WEPROT);
msp432_add_flash(t, MAIN_FLASH_BASE + banksize, banksize, MAIN_BANK1_WEPROT);
msp432_add_flash(t, INFO_FLASH_BASE, INFO_BANK_SIZE, INFO_BANK0_WEPROT);
msp432_add_flash(t, INFO_FLASH_BASE + INFO_BANK_SIZE, INFO_BANK_SIZE, INFO_BANK1_WEPROT);
target_add_commands(t, msp432_cmd_list, "MSP432P401x");
return true;
}
static bool msp432_sector_erase(target_flash_s *f, target_addr_t addr)
{
target_s *t = f->t;
msp432_flash_s *mf = (msp432_flash_s *)f;
uint32_t old_prot = msp432_sector_unprotect(mf, addr);
DEBUG_WARN("Flash protect: 0x%08" PRIX32 "\n", target_mem_read32(t, mf->flash_protect_register));
uint32_t regs[CORTEXM_GENERAL_REG_COUNT + CORTEX_FLOAT_REG_COUNT];
target_regs_read(t, regs);
regs[0] = addr;
DEBUG_INFO("Erasing sector at 0x%08" PRIX32 "\n", addr);
msp432_call_rom(t, mf->flash_erase_sector_fn, regs);
DEBUG_INFO("ROM return value: %" PRIu32 "\n", regs[0]);
target_mem_write32(t, mf->flash_protect_register, old_prot);
return regs[0] != 0;
}
static bool msp432_flash_erase(target_flash_s *f, target_addr_t addr, size_t len)
{
bool ret = true;
while (len) {
ret &= msp432_sector_erase(f, addr);
len -= f->blocksize;
if (len > f->blocksize)
len -= f->blocksize;
else
len = 0;
}
return ret;
}
static bool msp432_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len)
{
msp432_flash_s *mf = (msp432_flash_s *)f;
target_s *t = f->t;
target_mem_write(t, SRAM_WRITE_BUFFER, src, len);
uint32_t old_prot = msp432_sector_unprotect(mf, dest);
DEBUG_WARN("Flash protect: 0x%08" PRIX32 "\n", target_mem_read32(t, mf->flash_protect_register));
uint32_t regs[t->regs_size / sizeof(uint32_t)]; target_regs_read(t, regs);
regs[0] = SRAM_WRITE_BUFFER; regs[1] = dest; regs[2] = len;
DEBUG_INFO("Writing 0x%04" PRIX32 " bytes at 0x%08zu\n", dest, len);
msp432_call_rom(t, mf->flash_program_fn, regs);
target_mem_write32(t, mf->flash_protect_register, old_prot);
DEBUG_INFO("ROM return value: %" PRIu32 "\n", regs[0]);
return regs[0] != 0;
}
static bool msp432_cmd_erase_main(target_s *t, int argc, const char **argv)
{
(void)argc;
(void)argv;
uint32_t banksize = target_mem_read32(t, SYS_FLASH_SIZE) / 2U;
DEBUG_INFO("Bank Size: 0x%08" PRIX32 "\n", banksize);
bool result = true;
target_flash_s *f = target_flash_for_addr(t, MAIN_FLASH_BASE);
result &= msp432_flash_erase(f, MAIN_FLASH_BASE, banksize);
f = target_flash_for_addr(t, MAIN_FLASH_BASE + banksize);
result &= msp432_flash_erase(f, MAIN_FLASH_BASE + banksize, banksize);
return result;
}
static bool msp432_cmd_sector_erase(target_s *t, int argc, const char **argv)
{
if (argc < 2)
tc_printf(t, "usage: monitor sector_erase <addr>\n");
uint32_t addr = strtoul(argv[1], NULL, 0);
target_flash_s *f = target_flash_for_addr(t, addr);
if (f)
return msp432_sector_erase(f, addr);
tc_printf(t, "Invalid sector address\n");
return false;
}
static void msp432_call_rom(target_s *t, uint32_t address, uint32_t *regs)
{
target_mem_write16(t, WDT_A_WTDCTL, WDT_A_HOLD);
target_mem_write16(t, SRAM_CODE_BASE, CORTEX_THUMB_BREAKPOINT);
regs[CORTEX_REG_MSP] = SRAM_STACK_PTR;
regs[CORTEX_REG_LR] = SRAM_CODE_BASE | 1U;
regs[CORTEX_REG_PC] = address;
target_regs_write(t, regs);
target_halt_resume(t, false);
while (!target_halt_poll(t, NULL))
continue;
target_regs_read(t, regs);
}