Hi, I get a flash_init() failed trying to program an LPC55S69. Any Ideas?
Best, Rolf I: Launching gdb-server: openocd -c "gdb_port 50000" -c "tcl_port 50002" -c "telnet_port 50004" -s /usr/local/share/openocd/scripts -c "set FLASH_API_ADDRESS 0x030010f0" -f /Users/rolf/.vscode/extensions/onethinx.cortex-gdb-1.0.2/support/openocd-helpers.tcl -f interface/cmsis-dap.cfg -f target/lpc55xx.cfg -c "transport select swd" -c "adapter_khz 1000" I: Please check TERMINAL tab (gdb-server) for output from openocd I: Finished reading symbols from objdump: Time: 210 ms S: Open On-Chip Debugger 0.12.0+dev-00461-g6c7521591-dirty (2025-04-28-23:17) S: Licensed under GNU GPL v2 S: For bug reports, read S: http://openocd.org/doc/doxygen/bugs.html S: 0x030010f0 S: CDLiveWatchSetup S: Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'. S: adapter speed: 1000 kHz S: Info : Listening on port 50002 for tcl connections S: Info : Listening on port 50004 for telnet connections I: Finished reading symbols from nm: Time: 310 ms S: Info : CMSIS-DAP: SWD supported S: Info : CMSIS-DAP: Atomic commands supported S: Info : CMSIS-DAP: FW Version = 1.10 S: Info : CMSIS-DAP: Serial# = 1402B011 S: Info : CMSIS-DAP: Interface Initialised (SWD) S: Info : SWCLK/TCK = 1 SWDIO/TMS = 0 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1 S: Info : CMSIS-DAP: Interface ready S: Info : clock speed 1000 kHz S: Info : SWD DPIDR 0x6ba02477 S: Info : [lpc55xx.cpu] Cortex-M33 r0p3 processor detected S: Info : [lpc55xx.cpu] target has 8 breakpoints, 4 watchpoints S: Info : [lpc55xx.cpu] Examination succeed S: Info : starting gdb server for lpc55xx.cpu on 50000 S: Info : Listening on port 50000 for gdb connections S: Info : accepting 'gdb' connection on tcp/50000 S: Warn : [lpc55xx.cpu] target was in unknown state when halt was requested S: Info : SWD DPIDR 0x6ba02477 S: Info : [lpc55xx.cpu] external reset detected S: [lpc55xx.cpu] halted due to debug-request, current mode: Thread S: xPSR: 0x41000000 pc: 0x00019e02 psp: 0x20003ae8 S: Error: flash_init() failed Op 25 mrt 2024, om 21:53 heeft ger...@openocd.org het volgende geschreven: This is an automated email from Gerrit. "Paul Fertser <fercer...@gmail.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/8189 -- gerrit commit 7f1e8bd108cbd5ac686bf502d9665dd8544a664e Author: Paul Fertser <fercer...@gmail.com> Date: Wed Feb 7 14:02:23 2024 +0300 flash: nor: lpc55xx support Tested on LPC55S36 LPCXpresso board. Valgrind-clean. Signed-off-by: Paul Fertser <fercer...@gmail.com> Change-Id: Ifc73d5774838bf0455e4422d5a63b19eab949646 diff --git a/doc/openocd.texi b/doc/openocd.texi index 6b71fe869d..a7f381bf07 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -7158,6 +7158,25 @@ lpc2900 secure_jtag 0 @end deffn @end deffn +@deffn {Flash Driver} {lpc55xx} +This the flash driver using ROM API functions provided by NXP LPC55xx family +MCUs. + +The @var{lpc55xx} driver defines one mandatory parameter, the address of the +flash API ROM table. If 0 is passed the driver will try to auto-detect based on +device ID. + +The MCU will appear unresponsive when the first flash sector doesn't contain +valid firmware header (e.g. when the flash is blank), to attach to the target +@code{lpc55xx_start_debug_session} procedure is offered by the target config. + +Sector protection is not implemented. + +@example +flash bank $_FLASHNAME lpc55xx 0 0 0 0 0 0 +@end example +@end deffn + @deffn {Flash Driver} {mdr} This drivers handles the integrated NOR flash on Milandr Cortex-M based controllers. A known limitation is that the Info memory can't be diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 534a7a804e..23012e3c10 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -39,6 +39,7 @@ NOR_DRIVERS = \ %D%/lpc2000.c \ %D%/lpc288x.c \ %D%/lpc2900.c \ + %D%/lpc55xx.c \ %D%/lpcspifi.c \ %D%/max32xxx.c \ %D%/mdr.c \ diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h index a63b72c8fa..3851058f36 100644 --- a/src/flash/nor/driver.h +++ b/src/flash/nor/driver.h @@ -267,6 +267,7 @@ extern const struct flash_driver kinetis_ke_flash; extern const struct flash_driver lpc2000_flash; extern const struct flash_driver lpc288x_flash; extern const struct flash_driver lpc2900_flash; +extern const struct flash_driver lpc55xx_flash; extern const struct flash_driver lpcspifi_flash; extern const struct flash_driver max32xxx_flash; extern const struct flash_driver mdr_flash; diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 3157bd3292..fa1d4be554 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -44,6 +44,7 @@ static const struct flash_driver * const flash_drivers[] = { &lpc2000_flash, &lpc288x_flash, &lpc2900_flash, + &lpc55xx_flash, &lpcspifi_flash, &max32xxx_flash, &mdr_flash, diff --git a/src/flash/nor/lpc55xx.c b/src/flash/nor/lpc55xx.c new file mode 100644 index 0000000000..7138bffe8a --- /dev/null +++ b/src/flash/nor/lpc55xx.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/*************************************************************************** + * Copyright (C) 2024 Transcelestial Technologies PTE LTD * + * Paul Fertser <fercer...@gmail.com> * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include <helper/binarybuffer.h> +#include <target/algorithm.h> +#include <target/arm_opcodes.h> +#include <target/armv7m.h> + +/** + * @file + * Flash programming support for NXP LPC55xx series (tested on LPC55S36 1B) + */ + +#define LPC55XX_DEVICEID_REG 0x40000ff8 +#define LPC55XX_DIEID_REG 0x40000ffc + +#define API_FLASH_INIT 1 +#define API_FLASH_ERASE 2 +#define API_FLASH_PROGRAM 3 +#define API_FLASH_VERIFY_ERASE 4 + +#define BANK_WA_SIZE 1024 + +struct lpc55xx_flash_bank { + bool probed; + uint32_t flash_api_table; + struct working_area *wa; +}; + +FLASH_BANK_COMMAND_HANDLER(lpc55xx_flash_bank_command) +{ + struct lpc55xx_flash_bank *lpc55xx_info; + + if (CMD_ARGC < 7) + return ERROR_COMMAND_SYNTAX_ERROR; + + lpc55xx_info = calloc(1, sizeof(struct lpc55xx_flash_bank)); + + bank->driver_priv = lpc55xx_info; + lpc55xx_info->probed = false; + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[6], lpc55xx_info->flash_api_table); + + return ERROR_OK; +} + +static int lpc55xx_api_call(struct flash_bank *bank, unsigned int func, int regs, struct reg_param reg_params[]) +{ + struct lpc55xx_flash_bank *lpc55xx_info = bank->driver_priv; + struct target *target = bank->target; + struct armv7m_algorithm armv7m_info; + uint32_t func_addr; + uint32_t status; + + if (!lpc55xx_info->wa) { + if (target_alloc_working_area(target, BANK_WA_SIZE, &lpc55xx_info->wa) != ERROR_OK) { + LOG_ERROR("No working area specified, can not use flash API"); + return ERROR_FAIL; + } + + /* We'll be using this as LR target to halt after API function call */ + target_write_u32(target, lpc55xx_info->wa->address, ARMV5_T_BKPT(0)); + } + + target_read_u32(target, lpc55xx_info->flash_api_table + 4 * func, &func_addr); + + /* we have bkpt #0 there in the beginning of wa */ + init_reg_param(®_params[0], "lr", 32, PARAM_OUT); + buf_set_u32(reg_params[0].value, 0, 32, lpc55xx_info->wa->address | 1); + /* TODO what to set SP to?.. Can we count on "reset init" to leave it in suitable state? */ + /* + init_reg_param(®_params[1], "sp", 32, PARAM_OUT); + buf_set_u32(reg_params[1].value, 0, 32, lpc55xx_info->wa->address + BANK_WA_SIZE); + */ + /* dummy set to keep the count */ + init_reg_param(®_params[1], "r0", 32, PARAM_OUT); + buf_set_u32(reg_params[1].value, 0, 32, 0); + init_reg_param(®_params[2], "r0", 32, PARAM_IN_OUT); + buf_set_u32(reg_params[2].value, 0, 32, lpc55xx_info->wa->address + 4); + + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARM_MODE_THREAD; + target_run_algorithm(target, 0, NULL, regs, reg_params, func_addr - 1, 0, 10000, &armv7m_info); + + status = buf_get_u32(reg_params[2].value, 0, 32); + LOG_DEBUG("API call %d status 0x%8.8" PRIx32, func, status); + + for (int i = 0; i < regs; i++) + destroy_reg_param(®_params[i]); + + return status; +} + +static int lpc55xx_erase(struct flash_bank *bank, unsigned int first, + unsigned int last) +{ + struct reg_param reg_params[6]; + size_t sectors_size = 0; + int api_status; + + for (size_t i = first; i <= last; i++) + sectors_size += bank->sectors[i].size; + + init_reg_param(®_params[3], "r1", 32, PARAM_IN_OUT); + buf_set_u32(reg_params[3].value, 0, 32, bank->base + bank->sectors[first].offset); + init_reg_param(®_params[4], "r2", 32, PARAM_IN_OUT); + buf_set_u32(reg_params[4].value, 0, 32, sectors_size); + init_reg_param(®_params[5], "r3", 32, PARAM_IN_OUT); + buf_set_u32(reg_params[5].value, 0, 32, 'k' << 24 | 'e' << 16 | 'f' << 8 | 'l'); + + api_status = lpc55xx_api_call(bank, API_FLASH_ERASE, ARRAY_SIZE(reg_params), reg_params); + + return api_status == 0 ? ERROR_OK : ERROR_FLASH_OPERATION_FAILED; +} + +static int lpc55xx_erase_check(struct flash_bank *bank) +{ + struct reg_param reg_params[5]; + int retval = ERROR_OK; + int api_status; + + for (size_t i = 0; i < bank->num_sectors; i++) { + init_reg_param(®_params[3], "r1", 32, PARAM_IN_OUT); + buf_set_u32(reg_params[3].value, 0, 32, bank->base + bank->sectors[i].offset); + init_reg_param(®_params[4], "r2", 32, PARAM_IN_OUT); + buf_set_u32(reg_params[4].value, 0, 32, bank->sectors[i].size); + + api_status = lpc55xx_api_call(bank, API_FLASH_VERIFY_ERASE, ARRAY_SIZE(reg_params), reg_params); + + if (api_status == 0) { + bank->sectors[i].is_erased = true; + } else if (api_status == 105) { + bank->sectors[i].is_erased = false; + } else { + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + } + + return retval; +} + +static int lpc55xx_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct working_area *download_area; + struct target *target = bank->target; + struct reg_param reg_params[6]; + const size_t download_size = 8192; + int retval = ERROR_OK; + int api_status; + + if (target_alloc_working_area(target, download_size, &download_area) != ERROR_OK) { + LOG_ERROR("Failed to allocate working area for write buffer"); + return ERROR_FAIL; + } + + while (count != 0) { + size_t bytes_to_write = MIN(count, download_size); + + retval = target_write_buffer(target, download_area->address, bytes_to_write, buffer + offset); + if (retval != ERROR_OK) { + retval = ERROR_FLASH_OPERATION_FAILED; + goto end; + } + + init_reg_param(®_params[3], "r1", 32, PARAM_IN_OUT); + buf_set_u32(reg_params[3].value, 0, 32, offset); + init_reg_param(®_params[4], "r2", 32, PARAM_IN_OUT); + buf_set_u32(reg_params[4].value, 0, 32, download_area->address); + init_reg_param(®_params[5], "r3", 32, PARAM_IN_OUT); + buf_set_u32(reg_params[5].value, 0, 32, bytes_to_write); + + api_status = lpc55xx_api_call(bank, API_FLASH_PROGRAM, ARRAY_SIZE(reg_params), reg_params); + if (api_status != 0) { + LOG_ERROR("Error writing flash"); + retval = ERROR_FLASH_OPERATION_FAILED; + goto end; + } + + count -= bytes_to_write; + offset += bytes_to_write; + } + +end: + target_free_working_area(target, download_area); + + return retval; +} + + +static int lpc55xx_probe(struct flash_bank *bank) +{ + struct lpc55xx_flash_bank *lpc55xx_info = bank->driver_priv; + uint32_t device_id, sector_size, ver, base; + struct target *target = bank->target; + struct reg_param reg_params[3]; + uint32_t last_sector_size; + int api_status; + + target_read_u32(target, LPC55XX_DEVICEID_REG, &device_id); + LOG_DEBUG("Device ID: 0x%8.8" PRIx32, device_id); + target_read_u32(target, LPC55XX_DIEID_REG, &ver); + LOG_DEBUG("Chip revision ID and Number (DIEID): 0x%8.8" PRIx32, ver); + + if (lpc55xx_info->flash_api_table == 0) { + uint32_t rom_api_table; + + if ((device_id & 0xff0ffff0) == 0x500a11a0) { /* LPC553x */ + rom_api_table = 0x1302fc00; + /* the datasheet has table address but no information about + * device ID */ + /*} else if (device_id == XXXX) */ /* LPC55S6x/LPC55S2x/LPC552x */ /*{ + rom_api_table = 0x130010f0;*/ + } else { + LOG_ERROR("Unknown LPC55xx variant and ROM API table address not specified"); + return ERROR_FAIL; + } + + target_read_u32(target, rom_api_table + 4, &ver); + LOG_DEBUG("ROM API version: 0x%8.8" PRIx32, ver); + target_read_u32(target, rom_api_table + 0x10, &lpc55xx_info->flash_api_table); + } + + target_read_u32(target, lpc55xx_info->flash_api_table, &ver); + LOG_DEBUG("Flash API version: 0x%8.8" PRIx32, ver); + + api_status = lpc55xx_api_call(bank, API_FLASH_INIT, ARRAY_SIZE(reg_params), reg_params); + if (api_status != 0) { + LOG_ERROR("flash_init() failed"); + return ERROR_FLASH_OPERATION_FAILED; + } + + target_read_u32(target, lpc55xx_info->wa->address + 4, &base); + if (base != bank->base) { + LOG_ERROR("Flash bank address mismatch: API reported 0x%8.8" PRIx32, base); + return ERROR_FAIL; + } + + target_read_u32(target, lpc55xx_info->wa->address + 8, &bank->size); + target_read_u32(target, lpc55xx_info->wa->address + 0x14, §or_size); + + free(bank->sectors); + + bank->num_sectors = bank->size / sector_size; + last_sector_size = bank->size % sector_size; + if (last_sector_size != 0) + bank->num_sectors++; + bank->sectors = alloc_block_array(0, sector_size, bank->num_sectors); + if (!bank->sectors) + return ERROR_FAIL; + if (last_sector_size != 0) + bank->sectors[bank->num_sectors - 1].size = last_sector_size; + + target_read_u32(target, lpc55xx_info->wa->address + 0x10, &bank->write_start_alignment); + bank->write_end_alignment = bank->write_start_alignment; + + lpc55xx_info->probed = true; + + return ERROR_OK; +} + +static int lpc55xx_auto_probe(struct flash_bank *bank) +{ + struct lpc55xx_flash_bank *lpc55xx_info = bank->driver_priv; + if (lpc55xx_info->probed) + return ERROR_OK; + return lpc55xx_probe(bank); +} + +const struct flash_driver lpc55xx_flash = { + .name = "lpc55xx", + .flash_bank_command = lpc55xx_flash_bank_command, + .erase = lpc55xx_erase, + .write = lpc55xx_write, + .read = default_flash_read, + .probe = lpc55xx_probe, + .auto_probe = lpc55xx_auto_probe, + .erase_check = lpc55xx_erase_check, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/tcl/target/lpc55xx.cfg b/tcl/target/lpc55xx.cfg new file mode 100644 index 0000000000..829f2081aa --- /dev/null +++ b/tcl/target/lpc55xx.cfg @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +source [find target/swj-dp.tcl] + +adapter speed 1000 + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME lpc55xx +} + +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + # Cortex-M33 SWD DPIDR + set _CPUTAPID 0x6ba02477 +} + +swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian little -dap $_CHIPNAME.dap + +# Reserved RAM regions for ROM functions +# 0x14000000 -- 0x14003fff +# 0x30001000 -- 0x30007fff +# +# Use 16 KiB of SRAM2 (on LPC553x) for working area +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x4000 +} +$_TARGETNAME configure -work-area-phys 0x20008000 \ + -work-area-size $_WORKAREASIZE -work-area-backup 0 + +if { [info exists FLASH_API_ADDRESS] } { + set _FLASH_API_ADDRESS $FLASH_API_ADDRESS +} else { + set _FLASH_API_ADDRESS 0 +} + +# Using SRST is not recommended as that also resets the debug domain +if {![using_hla]} { + cortex_m reset_config sysresetreq +} + +proc init_reset { mode } { + if { $mode ne "run" } { + halt + wp 0x50000040 4 r + resume + } +} +$_TARGETNAME configure -event reset-end { catch { rwp 0x50000040 } } + +# Run this to initiate a debug session with a target that doesn't have +# valid firmware on flash +proc lpc55xx_start_debug_session { } { + global _CHIPNAME + $_CHIPNAME.dap apreg 2 0 0x21 + $_CHIPNAME.dap apreg 2 4 7 + $_CHIPNAME.cpu arp_examine +} + +flash bank $_TARGETNAME.flash lpc55xx 0x00000000 0 0 0 0 $_FLASH_API_ADDRESS --