target/lpc546xx: fix lpc546xx flash support (#801)
**Summary** Modifications to fix flash support on the lpc546xx: - fix IAP entrypoint to be `0x03000204`, not the value at that address - add a reset and attach sequence before erasing flash sectors. there's little documentation around this, but experimentally, erasing sector 0 fails when the ROM bootloader is mapped to it (on reset). stepping the chip once and attaching is enough to snap the chip out of it, permitting flash erase on sector 0. - add a few test commands to the lpc546xx table (read_uid, erase/write sector, etc). - write the magic CRC value when writing to sector 0 (`lpc_flash_write_magic_vect`). - move the lpc546xx probe to before the lpc43xx probe, to prevent getting the lpc546xx into Lockup when reading an illegal memory location during lpc43xx probing Fixes #786. I don't 100% understand the reset/load sequence of this part, but these changes are sufficient to program and debug the part now. I didn't do a detailed analysis of what pyocd (via st-link hardware adapter) and segger jlink do to handle the same, but both of those worked without modification, so there's some difference in the sequence they're using. **Testing** Verified I can now successfully erase and write an executable in sector 0 (and other sectors). Verified the new commands work correctly.
This commit is contained in:
parent
f796b774bd
commit
020600aa56
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue