Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
f878a4f
lpc43xx: `const`-correctness for the function signatures
dragonmux Oct 9, 2025
ddcbde8
lpc40xx: `const`-correctness for the function signatures
dragonmux Oct 9, 2025
6f8e3dd
lpc43xx: Fixed the macro prefixing and nomenclature
dragonmux Oct 10, 2025
091fa7c
lpc40xx: Fixed the macro prefixing and nomenclature
dragonmux Oct 10, 2025
b450e1e
lpc55xx: Fixed some links in the top-of-file comment
dragonmux Oct 10, 2025
e56db35
lpc40xx: Fixed some links in the top-of-file comment
dragonmux Oct 10, 2025
d54a163
lpc43xx: Added a top-of-file comment w/ datasheet and TRM links
dragonmux Oct 10, 2025
14ec0c5
lpc17xx: Fixed the top-of-file comment w/ some links and for style co…
dragonmux Oct 10, 2025
4a99cf7
lpc17xx: Fixed the macro prefixing and nomenclature
dragonmux Oct 10, 2025
e73ff9a
lpc17xx: `const`-correctness for the function signatures
dragonmux Oct 10, 2025
e3cef9d
lpc_common: Fixed the `target` and `flash` nomenclature
dragonmux Oct 11, 2025
a98b774
lpc_common: Fixed some clang-tidy warnings about maths readability
dragonmux Oct 11, 2025
3730a42
lpc_common: Fixed some missing suffixing on various numbers used
dragonmux Oct 11, 2025
72bc45f
lpc17xx: Made the IAP call code much more similar to the common impl …
dragonmux Oct 11, 2025
a3bb38c
lpc15xx: Fixed the macro prefixing and nomenclature
dragonmux Oct 11, 2025
f35d87d
lpc15xx: `const`-correctness for the function signatures
dragonmux Oct 11, 2025
4dc79a1
lpc11xx: Fixed the macro prefixing and nomenclature
dragonmux Oct 11, 2025
8dcbed9
lpc11xx: `const`-correctness for the function signatures
dragonmux Oct 11, 2025
587fcbf
lpc17xx: Added UID read command as the part supports the IAP action
dragonmux Oct 11, 2025
205cfac
lpc11xx: Normalised the code organisation a bit
dragonmux Oct 11, 2025
f4860b0
lpc11xx: Refactored the probe diagnostics for better Flash usage
dragonmux Oct 11, 2025
1732ff0
lpc_common: Moved the UID readout command into the common code for th…
dragonmux Oct 11, 2025
ef82f12
lpc_common: Restructured the IAP call routine and state too better re…
dragonmux Oct 12, 2025
4adf9fe
lpc40xx: Migrated the IAP invocations to using the common impl
dragonmux Oct 12, 2025
31b623c
lpc17xx: Migrated the IAP invocations to using the common impl
dragonmux Oct 12, 2025
2ae5400
lpc17xx: Switched the UID read command to the common impl
dragonmux Oct 12, 2025
4beb08f
lpc546xx: Fixed the macro prefixing and nomenclature
dragonmux Oct 12, 2025
360d8d0
lpc546xx: Added a top-of-file comment w/ datasheet and TRM links
dragonmux Oct 12, 2025
1042cff
lpc40xx: Fixed the top-of-file comment for style conformity
dragonmux Oct 13, 2025
42b507d
lpc15xx: Added a top-of-file comment w/ datasheet and TRM links
dragonmux Oct 13, 2025
617f426
lpc15xx: Refactored the device identification to provide names to the…
dragonmux Oct 13, 2025
cf22475
lpc11xx: Added a top-of-file comment w/ datasheet and TRM links
dragonmux Oct 14, 2025
3ac0386
lpc11xx: Given all LPC8xx part IDs names and their Flash and SRAM siz…
dragonmux Oct 15, 2025
d3ca07b
lpc11xx: Added all the SRAM and Flash variations for the LPC8xx serie…
dragonmux Oct 15, 2025
a4c7a78
lpc11xx: Given all LPC11xx part IDs names which show up at the LPC8xx…
dragonmux Oct 16, 2025
e2e44a6
lpc11xx: Added all the SRAM and Flash variations for the LPC8xx devic…
dragonmux Oct 16, 2025
6652b28
lpc11xx: Given all LPC111xx and LPC11Cxx part IDs names
dragonmux Oct 28, 2025
3febf98
lpc11xx: Given all LPC11Uxx part IDs names
dragonmux Oct 28, 2025
e98f1e3
lpc11xx: Given the LPC8N04 part ID a name, and its Flash and SRAM siz…
dragonmux Oct 28, 2025
abd2983
lpc11xx: Given the LPC11U6x part IDs names and added al the SRAM and …
dragonmux Oct 29, 2025
5d99128
lpc11xx: Given the LPC13xx part IDs names
dragonmux Oct 29, 2025
eb27e76
lpc11xx: Added all the SRAM and Flash variations for the LPC13xx seri…
dragonmux Oct 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
736 changes: 530 additions & 206 deletions src/target/lpc11xx.c

Large diffs are not rendered by default.

103 changes: 57 additions & 46 deletions src/target/lpc15xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* Copyright (C) 2011 Mike Smith <drziplok@me.com>
* Copyright (C) 2016 Gareth McMullin <gareth@blacksphere.co.nz>
* Copyright (C) 2016 David Lawrence <dlaw@markforged.com>
* Copyright (C) 2022-2025 1BitSquared <info@1bitsquared.com>
* Modified by Rachel Mant <git@dragonmux.network>
*
* 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
Expand All @@ -19,83 +21,92 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/*
* This file implements support for LPC15xx series devices, providing
* memory maps and Flash programming routines.
*
* References and details about the IAP variant used here:
* LPC15xx 32-bit ARM Cortex-M3 microcontroller, Product data sheet, Rev. 1.1
* https://www.nxp.com/docs/en/data-sheet/LPC15XX.pdf
* and (behind their login wall):
* UM10736 - LPC15xx User manual, Rev. 1.2
* https://www.nxp.com/webapp/Download?colCode=UM10736&location=null
*/

#include <string.h>
#include "general.h"
#include "target.h"
#include "target_internal.h"
#include "cortexm.h"
#include "lpc_common.h"

#define IAP_PGM_CHUNKSIZE 512U /* Should fit in RAM on any device */
#define LPC15xx_SRAM_SIZE_MIN 1024U
#define LPC15xx_SRAM_IAP_SIZE 32U /* IAP routines use 32 bytes at top of ram */

#define MIN_RAM_SIZE 1024U
#define RAM_USAGE_FOR_IAP_ROUTINES 32U /* IAP routines use 32 bytes at top of ram */
#define LPC15xx_IAP_ENTRYPOINT_LOCATION 0x03000205U
#define LPC15xx_IAP_RAM_BASE 0x02000000U

#define IAP_ENTRYPOINT 0x03000205U
#define IAP_RAM_BASE 0x02000000U
#define LPC15xx_IAP_PGM_CHUNKSIZE 512U /* Should fit in RAM on any device */

#define LPC15XX_DEVICE_ID 0x400743f8U
#define LPC15xx_SYSCON_BASE 0x4007400U
#define LPC15xx_SYSCON_DEVICE_ID0 (LPC15xx_SYSCON_BASE + 0x3f8U)

static bool lpc15xx_read_uid(target_s *target, int argc, const char **argv);
/* Taken from Device ID0 register in §3.6.51 of UM10736 rev 1.2, pg73 */
#define ID_LPC1517 0x00001517U
#define ID_LPC1518 0x00001518U
#define ID_LPC1519 0x00001519U
#define ID_LPC1547 0x00001547U
#define ID_LPC1548 0x00001548U
#define ID_LPC1549 0x00001549U

const command_s lpc15xx_cmd_list[] = {
{"readuid", lpc15xx_read_uid, "Read out the 16-byte UID."},
{NULL, NULL, NULL},
};

static void lpc15xx_add_flash(target_s *target, uint32_t addr, size_t len, size_t erasesize)
static void lpc15xx_add_flash(target_s *const target, const uint32_t addr, const size_t len, const size_t erasesize)
{
struct lpc_flash *flash = lpc_add_flash(target, addr, len, IAP_PGM_CHUNKSIZE);
flash->f.blocksize = erasesize;
flash->f.write = lpc_flash_write_magic_vect;
flash->iap_entry = IAP_ENTRYPOINT;
flash->iap_ram = IAP_RAM_BASE;
flash->iap_msp = IAP_RAM_BASE + MIN_RAM_SIZE - RAM_USAGE_FOR_IAP_ROUTINES;
struct lpc_flash *flash = lpc_add_flash(target, addr, len, LPC15xx_IAP_PGM_CHUNKSIZE);
flash->target_flash.blocksize = erasesize;
flash->target_flash.write = lpc_flash_write_magic_vect;
}

bool lpc15xx_probe(target_s *target)
bool lpc15xx_probe(target_s *const target)
{
/* read the device ID register */
const uint32_t device_id = target_mem32_read32(target, LPC15XX_DEVICE_ID);
/* Read the device ID register */
const uint32_t device_id = target_mem32_read32(target, LPC15xx_SYSCON_DEVICE_ID0);

uint32_t ram_size = 0;
switch (device_id) {
case 0x00001549U:
case 0x00001519U:
case ID_LPC1549:
case ID_LPC1519:
ram_size = 0x9000U;
break;
case 0x00001548U:
case 0x00001518U:
case ID_LPC1548:
case ID_LPC1518:
ram_size = 0x5000U;
break;
case 0x00001547U:
case 0x00001517U:
case ID_LPC1547:
case ID_LPC1517:
ram_size = 0x3000U;
break;
default:
return false;
}

/* Allocate the private structure needed for lpc_iap_call() to work */
lpc_priv_s *const priv = calloc(1, sizeof(*priv));
if (!priv) { /* calloc failed: heap exhaustion */
DEBUG_ERROR("calloc: failed in %s\n", __func__);
return false;
}
target->target_storage = priv;

/* Set the structure up for this target */
priv->iap_params = lpc_iap_params;
priv->iap_entry = LPC15xx_IAP_ENTRYPOINT_LOCATION;
priv->iap_ram = LPC15xx_IAP_RAM_BASE;
priv->iap_msp = LPC15xx_IAP_RAM_BASE + LPC15xx_SRAM_SIZE_MIN - LPC15xx_SRAM_IAP_SIZE;

/* Register Flash and RAM maps + target-specific commands */
target->driver = "LPC15xx";
target_add_ram32(target, 0x02000000, ram_size);
lpc15xx_add_flash(target, 0x00000000, 0x40000, 0x1000);
target_add_commands(target, lpc15xx_cmd_list, "LPC15xx");
return true;
}

static bool lpc15xx_read_uid(target_s *target, int argc, const char **argv)
{
(void)argc;
(void)argv;
struct lpc_flash *flash = (struct lpc_flash *)target->flash;
iap_result_s result = {0};
if (lpc_iap_call(flash, &result, IAP_CMD_READUID))
return false;
uint8_t uid[16U] = {0};
memcpy(&uid, result.values, sizeof(uid));
tc_printf(target, "UID: 0x");
for (uint32_t i = 0; i < sizeof(uid); ++i)
tc_printf(target, "%02x", uid[i]);
tc_printf(target, "\n");
lpc_add_commands(target);
return true;
}
143 changes: 45 additions & 98 deletions src/target/lpc17xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2012 Gareth McMullin <gareth@blacksphere.co.nz>
* Copyright (C) 2022-2025 1BitSquared <info@1bitsquared.com>
* Written by Akila Ravihansa Perera <ravihansa3000@gmail.com>
* Modified by Rachel Mant <git@dragonmux.network>
*
* 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
Expand All @@ -17,46 +20,40 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/*
* This file implements support for LPC17xx series devices, providing
* memory maps and Flash programming routines.
*
* References and details about the IAP variant used here:
* LPC1759/58/56/54/52/51 32-bit ARM Cortex-M3, Product data sheet, Rev. 8.7
* https://www.nxp.com/docs/en/data-sheet/LPC1759_58_56_54_52_51.pdf
* and (behind their login wall):
* UM10360 - LPC176x/5x User manual
* https://www.nxp.com/webapp/Download?colCode=UM10360&location=null
*/

#include "general.h"
#include "target.h"
#include "target_internal.h"
#include "cortex.h"
#include "cortexm.h"
#include "lpc_common.h"

/*
* For detailed documentation on how this code works and the IAP variant used here, see:
* https://www.nxp.com/docs/en/data-sheet/LPC1759_58_56_54_52_51.pdf
* and (behind their login wall):
* https://cache.nxp.com/secured/assets/documents/en/user-guide/UM10360.pdf?fileExt=.pdf
*/
#define LPC17xx_SRAM_SIZE_MIN 8192U // LPC1751
#define LPC17xx_SRAM_IAP_SIZE 32U // IAP routines use 32 bytes at top of ram

#define IAP_PGM_CHUNKSIZE 4096U
#define LPC17xx_IAP_ENTRYPOINT_LOCATION 0x1fff1ff1U
#define LPC17xx_IAP_RAM_BASE 0x10000000U

#define MIN_RAM_SIZE 8192U // LPC1751
#define RAM_USAGE_FOR_IAP_ROUTINES 32U // IAP routines use 32 bytes at top of ram
#define LPC17xx_IAP_PGM_CHUNKSIZE 4096U

#define IAP_ENTRYPOINT 0x1fff1ff1U
#define IAP_RAM_BASE 0x10000000U
#define LPC17xx_FLASH_NUM_SECTOR 30U

#define LPC17xx_MEMMAP UINT32_C(0x400fc040)
#define LPC17xx_MPU_BASE UINT32_C(0xe000ed90)
#define LPC17xx_MPU_CTRL (LPC17xx_MPU_BASE + 0x04U)

#define FLASH_NUM_SECTOR 30U

typedef struct iap_config {
uint32_t command;
uint32_t params[4];
} iap_config_s;

typedef struct BMD_ALIGN_DECL(4) iap_frame {
/* The start of an IAP stack frame is the opcode we set as the return point. */
uint16_t opcode;
/* There's then a hidden alignment field here, followed by the IAP call setup */
iap_config_s config;
} iap_frame_s;

typedef struct lpc17xx_priv {
lpc_priv_s base;
uint32_t mpu_ctrl_state;
uint32_t memmap_state;
} lpc17xx_priv_s;
Expand All @@ -65,21 +62,19 @@ static void lpc17xx_extended_reset(target_s *target);
static bool lpc17xx_enter_flash_mode(target_s *target);
static bool lpc17xx_exit_flash_mode(target_s *target);
static bool lpc17xx_mass_erase(target_s *target, platform_timeout_s *print_progess);
iap_status_e lpc17xx_iap_call(
target_s *target, iap_result_s *result, platform_timeout_s *print_progess, iap_cmd_e cmd, ...);

static void lpc17xx_add_flash(target_s *target, uint32_t addr, size_t len, size_t erasesize, uint8_t base_sector)
static size_t lpc17xx_iap_params(iap_cmd_e cmd);

static void lpc17xx_add_flash(
target_s *const target, const uint32_t addr, const size_t len, const size_t erasesize, const uint8_t base_sector)
{
lpc_flash_s *flash = lpc_add_flash(target, addr, len, IAP_PGM_CHUNKSIZE);
flash->f.blocksize = erasesize;
lpc_flash_s *const flash = lpc_add_flash(target, addr, len, LPC17xx_IAP_PGM_CHUNKSIZE);
flash->target_flash.blocksize = erasesize;
flash->base_sector = base_sector;
flash->f.write = lpc_flash_write_magic_vect;
flash->iap_entry = IAP_ENTRYPOINT;
flash->iap_ram = IAP_RAM_BASE;
flash->iap_msp = IAP_RAM_BASE + MIN_RAM_SIZE - RAM_USAGE_FOR_IAP_ROUTINES;
flash->target_flash.write = lpc_flash_write_magic_vect;
}

bool lpc17xx_probe(target_s *target)
bool lpc17xx_probe(target_s *const target)
{
if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) != CORTEX_M3)
return false;
Expand All @@ -92,18 +87,24 @@ bool lpc17xx_probe(target_s *target)
target_halt_request(target);

/* Allocate private storage so the flash mode entry/exit routines can save state */
lpc17xx_priv_s *priv = calloc(1, sizeof(*priv));
lpc17xx_priv_s *const priv = calloc(1, sizeof(*priv));
if (!priv) { /* calloc failed: heap exhaustion */
DEBUG_ERROR("calloc: failed in %s\n", __func__);
return false;
}
target->target_storage = priv;

/* Set the structure up for this target */
priv->base.iap_params = lpc17xx_iap_params;
priv->base.iap_entry = LPC17xx_IAP_ENTRYPOINT_LOCATION;
priv->base.iap_ram = LPC17xx_IAP_RAM_BASE;
priv->base.iap_msp = LPC17xx_IAP_RAM_BASE + LPC17xx_SRAM_SIZE_MIN - LPC17xx_SRAM_IAP_SIZE;

/* Prepare Flash mode */
lpc17xx_enter_flash_mode(target);
/* Read the Part ID */
iap_result_s result;
lpc17xx_iap_call(target, &result, NULL, IAP_CMD_PARTID);
lpc_iap_call(target, &result, IAP_CMD_PARTID);
/* Transition back to normal mode and resume the target */
lpc17xx_exit_flash_mode(target);
target_halt_resume(target, false);
Expand Down Expand Up @@ -147,6 +148,7 @@ bool lpc17xx_probe(target_s *target)
target_add_ram32(target, 0x20080000U, 0x4000U);
lpc17xx_add_flash(target, 0x00000000U, 0x10000U, 0x1000U, 0);
lpc17xx_add_flash(target, 0x00010000U, 0x70000U, 0x8000U, 16);
lpc_add_commands(target);
return true;
}

Expand All @@ -172,19 +174,20 @@ static bool lpc17xx_exit_flash_mode(target_s *const target)

static bool lpc17xx_mass_erase(target_s *const target, platform_timeout_s *const print_progess)
{
(void)print_progess;
iap_result_s result;

if (lpc17xx_iap_call(target, &result, print_progess, IAP_CMD_PREPARE, 0, FLASH_NUM_SECTOR - 1U)) {
if (lpc_iap_call(target, &result, IAP_CMD_PREPARE, 0, LPC17xx_FLASH_NUM_SECTOR - 1U)) {
DEBUG_ERROR("%s: prepare failed %" PRIu32 "\n", __func__, result.return_code);
return false;
}

if (lpc17xx_iap_call(target, &result, print_progess, IAP_CMD_ERASE, 0, FLASH_NUM_SECTOR - 1U, CPU_CLK_KHZ)) {
if (lpc_iap_call(target, &result, IAP_CMD_ERASE, 0, LPC17xx_FLASH_NUM_SECTOR - 1U, CPU_CLK_KHZ)) {
DEBUG_ERROR("%s: erase failed %" PRIu32 "\n", __func__, result.return_code);
return false;
}

if (lpc17xx_iap_call(target, &result, print_progess, IAP_CMD_BLANKCHECK, 0, FLASH_NUM_SECTOR - 1U)) {
if (lpc_iap_call(target, &result, IAP_CMD_BLANKCHECK, 0, LPC17xx_FLASH_NUM_SECTOR - 1U)) {
DEBUG_ERROR("%s: blankcheck failed %" PRIu32 "\n", __func__, result.return_code);
return false;
}
Expand All @@ -196,7 +199,7 @@ static bool lpc17xx_mass_erase(target_s *const target, platform_timeout_s *const
* Target has been reset, make sure to remap the boot ROM
* from 0x00000000 leaving the user flash visible
*/
static void lpc17xx_extended_reset(target_s *target)
static void lpc17xx_extended_reset(target_s *const target)
{
/*
* Transition the memory map to user mode (if it wasn't already) to ensure
Expand All @@ -218,59 +221,3 @@ static size_t lpc17xx_iap_params(const iap_cmd_e cmd)
return 0U;
}
}

iap_status_e lpc17xx_iap_call(
target_s *const target, iap_result_s *const result, platform_timeout_s *const print_progess, iap_cmd_e cmd, ...)
{
/* Set up our IAP frame with the break opcode and command to run */
iap_frame_s frame = {
.opcode = CORTEX_THUMB_BREAKPOINT,
.config = {.command = cmd},
};

/* Fill out the remainder of the parameters */
const size_t params_count = lpc17xx_iap_params(cmd);
va_list params;
va_start(params, cmd);
for (size_t i = 0; i < params_count; ++i)
frame.config.params[i] = va_arg(params, uint32_t);
va_end(params);
for (size_t i = params_count; i < 4U; ++i)
frame.config.params[i] = 0U;

/* Copy the structure to RAM */
target_mem32_write(target, IAP_RAM_BASE, &frame, sizeof(iap_frame_s));
const uint32_t iap_params_addr = IAP_RAM_BASE + offsetof(iap_frame_s, config);

/* Set up for the call to the IAP ROM */
uint32_t regs[CORTEXM_GENERAL_REG_COUNT];
target_regs_read(target, regs);
/* Point r0 to the start of the config block */
regs[0] = iap_params_addr;
/* And r1 to the same so we re-use the same memory for the results */
regs[1U] = iap_params_addr;
/* Set the top of stack to the top of the RAM block we're using */
regs[CORTEX_REG_MSP] = IAP_RAM_BASE + MIN_RAM_SIZE;
/* Point the return address to our breakpoint opcode (thumb mode) */
regs[CORTEX_REG_LR] = IAP_RAM_BASE | 1U;
/* And set the program counter to the IAP ROM entrypoint */
regs[CORTEX_REG_PC] = IAP_ENTRYPOINT;
target_regs_write(target, regs);

platform_timeout_s timeout;
platform_timeout_set(&timeout, 500U);
/* Start the target and wait for it to halt again */
target_halt_resume(target, false);
while (!target_halt_poll(target, NULL)) {
if (print_progess)
target_print_progress(print_progess);
else if (cmd == IAP_CMD_PARTID && platform_timeout_is_expired(&timeout)) {
target_halt_request(target);
return IAP_STATUS_INVALID_COMMAND;
}
}

/* Copy back just the results */
target_mem32_read(target, result, iap_params_addr, sizeof(iap_result_s));
return result->return_code;
}
Loading
Loading