diff --git a/src/target/cortexm.c b/src/target/cortexm.c index d7c7289..78f5330 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -445,8 +445,16 @@ bool cortexm_probe(ADIv5_AP_t *ap) PROBE(lpc11xx_probe); /* LPC24C11 */ PROBE(lpc43xx_probe); } else if (ap->ap_partno == 0x4c4) { /* Cortex-M4 ROM */ - PROBE(lpc43xx_probe); + /* The LPC546xx and LPC43xx parts present with the same AP ROM Part + Number, so we need to probe both. Unfortunately, when probing for + the LPC43xx when the target is actually an LPC546xx, the memory + location checked is illegal for the LPC546xx and puts the chip into + Lockup, requiring a RST pulse to recover. Instead, make sure to + probe for the LPC546xx first, which experimentally doesn't harm + LPC43xx detection. */ PROBE(lpc546xx_probe); + + PROBE(lpc43xx_probe); PROBE(kinetis_probe); /* Older K-series */ } else if (ap->ap_partno == 0x4cb) { /* Cortex-M23 ROM */ PROBE(gd32f1_probe); /* GD32E23x uses GD32F1 peripherals */ diff --git a/src/target/lpc546xx.c b/src/target/lpc546xx.c index 1e0ad93..00fc6d4 100644 --- a/src/target/lpc546xx.c +++ b/src/target/lpc546xx.c @@ -27,36 +27,53 @@ #define LPC546XX_CHIPID 0x40000FF8 -#define IAP_ENTRYPOINT_LOCATION 0x03000204 +#define IAP_ENTRYPOINT_LOCATION 0x03000204 #define LPC546XX_ETBAHB_SRAM_BASE 0x20000000 -#define LPC546XX_ETBAHB_SRAM_SIZE (160*1024) + +/* only SRAM0 bank is enabled after reset */ +#define LPC546XX_ETBAHB_SRAM_SIZE (64 * 1024) #define LPC546XX_WDT_MODE 0x4000C000 -#define LPC546XX_WDT_CNT 0x4000C004 +#define LPC546XX_WDT_CNT 0x4000C004 #define LPC546XX_WDT_FEED 0x4000C008 #define LPC546XX_WDT_PERIOD_MAX 0xFFFFFF #define LPC546XX_WDT_PROTECT (1 << 4) -#define IAP_RAM_SIZE LPC546XX_ETBAHB_SRAM_SIZE -#define IAP_RAM_BASE LPC546XX_ETBAHB_SRAM_BASE +#define IAP_RAM_SIZE LPC546XX_ETBAHB_SRAM_SIZE +#define IAP_RAM_BASE LPC546XX_ETBAHB_SRAM_BASE -#define IAP_PGM_CHUNKSIZE 4096 +#define IAP_PGM_CHUNKSIZE 4096 -#define FLASH_NUM_SECTOR 15 - -static bool lpc546xx_cmd_erase(target *t, int argc, const char *argv[]); +static bool lpc546xx_cmd_erase_mass(target *t, int argc, const char *argv[]); +static bool lpc546xx_cmd_erase_sector(target *t, int argc, const char *argv[]); +static bool lpc546xx_cmd_read_partid(target *t, int argc, const char *argv[]); +static bool lpc546xx_cmd_read_uid(target *t, int argc, const char *argv[]); +static bool lpc546xx_cmd_reset_attach(target *t, int argc, const char *argv[]); static bool lpc546xx_cmd_reset(target *t, int argc, const char *argv[]); +static bool lpc546xx_cmd_write_sector(target *t, int argc, const char *argv[]); + +static void lpc546xx_reset_attach(target *t); static int lpc546xx_flash_init(target *t); static int lpc546xx_flash_erase(struct target_flash *f, target_addr addr, size_t len); -static void lpc546xx_set_internal_clock(target *t); static void lpc546xx_wdt_set_period(target *t); static void lpc546xx_wdt_pet(target *t); const struct command_s lpc546xx_cmd_list[] = { - {"erase_mass", lpc546xx_cmd_erase, "Erase entire flash memory"}, - {"reset", lpc546xx_cmd_reset, "Reset target"}, - {NULL, NULL, NULL} + { "erase_mass", lpc546xx_cmd_erase_mass, "Erase entire flash memory" }, + { "erase_sector", lpc546xx_cmd_erase_sector, + "Erase a sector by number" }, + { "read_partid", lpc546xx_cmd_read_partid, + "Read out the 32-bit part ID using IAP." }, + { "read_uid", lpc546xx_cmd_read_uid, "Read out the 16-byte UID." }, + { "reset_attach", lpc546xx_cmd_reset_attach, + "Reset target. Reset debug registers. Re-attach debugger. This restores " + "the chip to the very start of program execution, after the ROM " + "bootloader." }, + { "reset", lpc546xx_cmd_reset, "Reset target" }, + { "write_sector", lpc546xx_cmd_write_sector, + "Write incrementing data 8-bit values across a previously erased sector" }, + { NULL, NULL, NULL } }; void lpc546xx_add_flash(target *t, uint32_t iap_entry, @@ -65,6 +82,11 @@ void lpc546xx_add_flash(target *t, uint32_t iap_entry, { struct lpc_flash *lf = lpc_add_flash(t, addr, len); lf->f.erase = lpc546xx_flash_erase; + + /* LPC546xx devices require the checksum value written into the vector table + in sector 0 */ + lf->f.write = lpc_flash_write_magic_vect; + lf->f.blocksize = erasesize; lf->f.buf_size = IAP_PGM_CHUNKSIZE; lf->bank = 0; @@ -78,7 +100,6 @@ void lpc546xx_add_flash(target *t, uint32_t iap_entry, bool lpc546xx_probe(target *t) { uint32_t chipid; - uint32_t iap_entry; uint32_t flash_size; chipid = target_mem_read32(t, LPC546XX_CHIPID); @@ -132,10 +153,12 @@ bool lpc546xx_probe(target *t) return false; } - iap_entry = target_mem_read32(t, - IAP_ENTRYPOINT_LOCATION); - lpc546xx_add_flash(t, iap_entry, 0, 0x0, - flash_size, 0x8000); + lpc546xx_add_flash(t, IAP_ENTRYPOINT_LOCATION, 0, 0x0, flash_size, + 0x8000); + + /* Note: upper 96kB is only usable after enabling the appropriate control + register bits, see LPC546xx User Manual: 7.5.19 AHB Clock Control register 0 + */ target_add_ram(t, 0x20000000, 0x28000); target_add_commands(t, lpc546xx_cmd_list, "Lpc546xx"); t->target_options |= CORTEXM_TOPT_INHIBIT_SRST; @@ -143,7 +166,88 @@ bool lpc546xx_probe(target *t) return true; } -/* Reset all major systems _except_ debug */ +static void lpc546xx_reset_attach(target *t) +{ + /* To reset the LPC546xx into a usable state, we need to reset and let it + step once, then attach the debug probe again. Otherwise the ROM bootloader + is mapped to address 0x0, we can't perform flash operations on sector 0, + and reading memory from sector 0 will return the contents of the ROM + bootloader, not the flash */ + target_reset(t); + target_halt_resume(t, false); + cortexm_attach(t); +} + +static bool lpc546xx_cmd_erase_mass(target *t, int argc, const char *argv[]) +{ + (void)argc; + (void)argv; + + int result = lpc546xx_flash_erase(t->flash, t->flash->start, + t->flash->length); + + if (result != 0) { + tc_printf(t, "Error erasing flash: %d\n", result); + return false; + } + + tc_printf(t, "Erase OK.\n"); + + return true; +} + +static bool lpc546xx_cmd_erase_sector(target *t, int argc, const char *argv[]) +{ + if (argc > 1) { + uint32_t sector_addr = strtoul(argv[1], NULL, 0); + sector_addr *= t->flash->blocksize; + int retval = lpc546xx_flash_erase(t->flash, sector_addr, 1); + return retval == 0; + } + return -1; +} + +static bool lpc546xx_cmd_read_partid(target *t, int argc, const char *argv[]) +{ + (void)argc; + (void)argv; + struct lpc_flash *f = (struct lpc_flash *)t->flash; + uint32_t partid[4]; + if (lpc_iap_call(f, partid, IAP_CMD_PARTID)) + return false; + tc_printf(t, "PART ID: 0x%08x\n", partid[0]); + return true; +} + +static bool lpc546xx_cmd_read_uid(target *t, int argc, const char *argv[]) +{ + (void)argc; + (void)argv; + struct lpc_flash *f = (struct lpc_flash *)t->flash; + uint8_t uid[16]; + if (lpc_iap_call(f, uid, IAP_CMD_READUID)) + return false; + tc_printf(t, "UID: 0x"); + for (uint32_t i = 0; i < sizeof(uid); ++i) + tc_printf(t, "%02x", uid[i]); + tc_printf(t, "\n"); + return true; +} + +/* Reset everything, including debug; single step past the ROM bootloader so the +system is in a sane state */ +static bool lpc546xx_cmd_reset_attach(target *t, int argc, const char *argv[]) +{ + (void)argc; + (void)argv; + + lpc546xx_reset_attach(t); + + return true; +} + +/* Reset all major systems _except_ debug. Note that this will leave the system +with the ROM bootloader mapped to 0x0 */ static bool lpc546xx_cmd_reset(target *t, int argc, const char *argv[]) { (void)argc; @@ -160,38 +264,44 @@ static bool lpc546xx_cmd_reset(target *t, int argc, const char *argv[]) return true; } -static bool lpc546xx_cmd_erase(target *t, int argc, const char *argv[]) +static bool lpc546xx_cmd_write_sector(target *t, int argc, const char *argv[]) { - (void)argc; - (void)argv; + if (argc > 1) { + const uint32_t sector_size = t->flash->blocksize; + uint32_t sector_addr = strtoul(argv[1], NULL, 0); + sector_addr *= sector_size; - lpc546xx_flash_init(t); - struct lpc_flash *f = (struct lpc_flash *)t->flash; + int retval = lpc546xx_flash_erase(t->flash, sector_addr, 1); + if (retval != 0) { + return retval; + } - if (lpc_iap_call(f, NULL, IAP_CMD_PREPARE, 0, FLASH_NUM_SECTOR-1)) - return false; + uint8_t *buf = calloc(1, sector_size); + for (uint32_t i = 0; i < sector_size; i++) { + buf[i] = i & 0xff; + } - if (lpc_iap_call(f, NULL, IAP_CMD_ERASE, 0, FLASH_NUM_SECTOR-1, CPU_CLK_KHZ)) - return false; + retval = lpc_flash_write_magic_vect(t->flash, sector_addr, buf, + sector_size); - tc_printf(t, "Erase OK.\n"); + free(buf); - return true; + return retval == 0; + } + return -1; } static int lpc546xx_flash_init(target *t) { + /* Reset the chip. It's unfortunate but we need to make sure the ROM + bootloader is no longer mapped to 0x0 or flash blank check won't work after + erasing that sector. Resetting will also set the main clock back to default + 12MHZ FRO; that value is required for some IAP routines. */ + lpc546xx_reset_attach(t); + /* Deal with WDT */ lpc546xx_wdt_set_period(t); - /* /\* Force internal clock *\/ */ - lpc546xx_set_internal_clock(t); - - /* Initialize flash IAP */ - struct lpc_flash *f = (struct lpc_flash *)t->flash; - if (lpc_iap_call(f, NULL, IAP_CMD_INIT)) - return -1; - return 0; } @@ -203,12 +313,6 @@ static int lpc546xx_flash_erase(struct target_flash *tf, target_addr addr, size_ return lpc_flash_erase(tf, addr, len); } -static void lpc546xx_set_internal_clock(target *t) -{ - /* Switch to 12 Mhz FRO */ - target_mem_write32(t, 0x40000000 + 0x248, 0); -} - static void lpc546xx_wdt_set_period(target *t) { /* Check if WDT is on */ diff --git a/src/target/lpc_common.c b/src/target/lpc_common.c index 52bf87f..b16c22c 100644 --- a/src/target/lpc_common.c +++ b/src/target/lpc_common.c @@ -109,6 +109,11 @@ enum iap_status lpc_iap_call(struct lpc_flash *f, void *result, enum iap_cmd cmd if (result != NULL) memcpy(result, param.result, sizeof(param.result)); + if (param.status != IAP_STATUS_CMD_SUCCESS) { + DEBUG_WARN("IAP failure code %d for cmd %d\n", + (enum iap_status)param.status, cmd); + } + return param.status; } @@ -165,8 +170,10 @@ int lpc_flash_write_magic_vect(struct target_flash *f, uint32_t *w = (uint32_t *)src; uint32_t sum = 0; + /* compute checksum of first 7 vectors */ for (unsigned i = 0; i < 7; i++) sum += w[i]; + /* two's complement is written to 8'th vector */ w[7] = ~sum + 1; } return lpc_flash_write(f, dest, src, len);