|
From: openocd-gerrit <ope...@us...> - 2026-09-12 08:10:56
|
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Main OpenOCD repository".
The branch, master has been updated
via f3cedea436592b3bff240a412fba58850129334d (commit)
via e975d571bc5ebd703700ef105b11e6d60768a630 (commit)
from 0cc2812da831c57a4cc51f8f2f24de2ad8f324b4 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit f3cedea436592b3bff240a412fba58850129334d
Author: Sameer Srivastava <s-s...@ti...>
Date: Sun Mar 8 12:15:13 2026 +0530
flash/nor: add am13e230x flash driver
Flashing uses the am13e230x flash loader that runs from SRAM, matching
the CC26xx pattern. The algorithm is loaded into SRAM and started via
target_start_algorithm. OpenOCD streams sector data into two ping-pong
buffers; the algorithm erases each sector and programs it using direct
FCTL register access.
Add AM13 flash driver section in documentation, detailing flash regions,
example commands and the immutability of the first NONMAIN sectors in
each bank.
Change-Id: Ifb910a545146514574fb4efecc3ad5aec38e2576
Signed-off-by: Sameer Srivastava <s-s...@ti...>
Reviewed-on: https://review.openocd.org/c/openocd/+/9527
Reviewed-by: Nishanth Menon <nm...@ti...>
Reviewed-by: Tomas Vanek <va...@fb...>
Tested-by: jenkins
diff --git a/doc/openocd.texi b/doc/openocd.texi
index cb08471be..5e24301e3 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -6840,6 +6840,70 @@ the flash.
@end deffn
@end deffn
+@deffn {Flash Driver} {am13}
+@cindex am13
+@cindex AM13E230X
+All Texas Instruments AM13E230X ARM Cortex-M33 microcontrollers include
+internal flash. The @var{am13} flash driver probes the device by reading
+the JTAGIDCODE (DID) register and the SRAMFLASH info register to
+determine the flash and SRAM sizes automatically.
+
+Two flash regions are supported:
+
+@itemize @bullet
+@item @b{MAIN} flash: starts at address @code{0x00000000}; size is
+auto-detected (typically 256 KB or 512 KB in 2 KB sectors).
+@item @b{NONMAIN} flash: starts at address @code{0x60100000}; sector 0 of each
+bank's NONMAIN region (NM0, at @code{0x60100000} and @code{0x60101000}) is
+BCR-protected and skipped by OpenOCD. The reprogrammable sectors are NM1 of
+each bank (@code{0x60100800} and @code{0x60101800}); size is
+@math{num_banks * 4096} bytes.
+@end itemize
+
+@b{Write strategy}: the driver loads a ping-pong flash loader algorithm
+into SRAM at @code{0x20000000} for fast sector-at-a-time writes. If
+the working area is unavailable (e.g., a RAM-resident application
+occupies SRAM) the driver falls back to slower word-by-word direct
+register writes. The working area @b{must} be configured at
+@code{0x20000000}; this is the default in @file{target/ti/am13e230x.cfg}.
+
+@b{Erase}: performed directly through the NVMNW flash controller
+registers from the debug probe. The GSC flash semaphore is acquired
+before each erase operation and released on completion.
+
+@b{Note}: the flash controller auto-protects after every flash
+operation. OpenOCD automatically un-protects the required sectors
+before each erase or write and does not expose separate protect
+commands.
+
+@b{Example} --- typical @file{flash bank} declarations (already included
+in @file{target/ti/am13e230x.cfg}):
+
+@example
+flash bank $_FLASHNAME.main am13 0x00000000 0 0 0 $_TARGETNAME
+flash bank $_FLASHNAME.nonmain am13 0x60100000 0 0 0 $_TARGETNAME
+@end example
+
+@b{To erase and program MAIN flash}:
+
+@example
+halt
+flash erase_sector 0 0 last
+flash write_image firmware.bin 0x0
+reset run
+@end example
+
+@b{To erase and program NONMAIN flash} (BANK1 NM1, user application storage):
+
+@example
+halt
+flash erase_sector 1 3 3
+flash write_image nonmain.bin 0x60101800
+reset run
+@end example
+
+@end deffn
+
@deffn {Flash Driver} {artery}
@cindex artery
This driver supports Artery Technology devices from the following series:
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 66f784126..9a46cc3bc 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -11,6 +11,7 @@ noinst_LTLIBRARIES += %D%/libocdflashnor.la
NOR_DRIVERS = \
%D%/aduc702x.c \
%D%/aducm360.c \
+ %D%/am13e230x.c \
%D%/ambiqmicro.c \
%D%/artery.c \
%D%/at91sam4.c \
diff --git a/src/flash/nor/am13e230x.c b/src/flash/nor/am13e230x.c
new file mode 100644
index 000000000..e44fec739
--- /dev/null
+++ b/src/flash/nor/am13e230x.c
@@ -0,0 +1,758 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * NOR flash driver for AM13E230X class of uC from Texas Instruments.
+ *
+ * Erase uses direct register writes via the debug probe.
+ * Write uses an on-device algorithm loaded into SRAM
+ * (see contrib/loaders/flash/am13e230x/ and flash.h).
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <helper/binarybuffer.h>
+#include <helper/bits.h>
+#include <helper/time_support.h>
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+
+/* Region memory map */
+#define AM13_FLASH_BASE_MAIN 0x00000000
+#define AM13_FLASH_BASE_NONMAIN 0x60100000
+#define AM13_NONMAIN_SIZE_PER_BANK 0x1000
+
+/*
+ * Factory region registers (at 0x60111000, in the non-main flash area).
+ *
+ * AM13_DID (0x60111004) is the DEVICEID / JTAGIDCODE register ("the
+ * JTAGIDCODE that comes from the Ramp system", per TRM SPRUJF2A Table
+ * 18-1). Its manufacturer ID field lives in bits[11:1] per the JTAG
+ * IEEE 1149.1 encoding, which is what am13_probe() validates.
+ *
+ * This is distinct from PARTIDL/PARTIDH at 0x0005D008, which encode
+ * package type and quality grade. Those fields are not used here
+ * because they vary by package variant and are not needed to confirm
+ * that the target is an AM13E230X-class device.
+ */
+#define AM13_FACTORYREGION 0x60111000
+#define AM13_TRACEID (AM13_FACTORYREGION + 0x000)
+#define AM13_DID (AM13_FACTORYREGION + 0x004) /* JTAGIDCODE */
+#define AM13_SRAMFLASH (AM13_FACTORYREGION + 0x01C)
+
+/* Flash controller (NVMNW) registers */
+#define FLASH_CONTROL_BASE 0x40042000
+#define FCTL_REG_CMDEXEC (FLASH_CONTROL_BASE + 0x1100)
+#define FCTL_REG_CMDTYPE (FLASH_CONTROL_BASE + 0x1104)
+#define FCTL_REG_CMDADDR (FLASH_CONTROL_BASE + 0x1120)
+#define FCTL_REG_CMDBYTEN (FLASH_CONTROL_BASE + 0x1124)
+#define FCTL_REG_CMDDATAINDEX (FLASH_CONTROL_BASE + 0x112C)
+#define FCTL_REG_CMDDATA0 (FLASH_CONTROL_BASE + 0x1130)
+#define FCTL_REG_CMDWEPROTA (FLASH_CONTROL_BASE + 0x11D0)
+#define FCTL_REG_CMDWEPROTB (FLASH_CONTROL_BASE + 0x11D4)
+#define FCTL_REG_CMDWEPROTNM (FLASH_CONTROL_BASE + 0x1210)
+#define FCTL_REG_STATCMD (FLASH_CONTROL_BASE + 0x13D0)
+
+/* GSC flash semaphore registers */
+#define GSC_BASE 0x40046000
+#define GSC_REG_FPC_FLSEMREQ (GSC_BASE + 0x1800)
+#define GSC_REG_FPC_FLSEMCLR (GSC_BASE + 0x1804)
+
+/* STATCMD bits */
+#define FCTL_STATCMD_CMDDONE 0x00000001
+#define FCTL_STATCMD_CMDPASS 0x00000002
+#define FCTL_STATCMD_CMDINPROGRESS 0x00000004
+
+/* CMDEXEC / CMDTYPE constants */
+#define FCTL_CMDEXEC_EXECUTE 0x00000001
+#define FCTL_CMDTYPE_PROGRAM 0x00000001
+#define FCTL_CMDTYPE_ERASE 0x00000002
+#define FCTL_CMDTYPE_CLEARSTATUS 0x00000005
+#define FCTL_CMDTYPE_SIZE_ONEWORD 0x00000000
+#define FCTL_CMDTYPE_SIZE_SECTOR 0x00000040
+
+#define AM13_FLASH_WORD_SIZE 16
+
+#define AM13_FLASH_TIMEOUT_MS 8000
+#define AM13_SECTOR_SIZE_BYTES 0x800
+#define TI_MANUFACTURER_ID 0x17
+
+/* Algorithm is loaded at the base of SRAM */
+#define AM13_ALGO_BASE 0x20000000
+
+/* Parameter blocks (two, for ping-pong) */
+#define AM13_ALGO_PARAMS_0 0x20002000
+#define AM13_ALGO_PARAMS_1 0x20002014
+
+/* Data buffers (each one sector = 2 KB) */
+#define AM13_ALGO_BUFFER_0 0x20002100
+#define AM13_ALGO_BUFFER_1 0x20002900
+
+/* Total working area needed */
+#define AM13_ALGO_WORKING_SIZE (AM13_ALGO_BUFFER_1 + 0x800 - AM13_ALGO_BASE)
+
+/* Handshake values (must match loader main.c) */
+#define AM13_BUFFER_EMPTY 0x00000000
+#define AM13_BUFFER_FULL 0xFFFFFFFF
+
+/* Offset of the status field within flash_params */
+#define AM13_STATUS_OFFSET 0x0C
+
+/* Commands (must match loader main.c) */
+#define AM13_CMD_NO_ACTION 0
+#define AM13_CMD_PROGRAM 1
+#define AM13_CMD_ERASE_AND_PROGRAM 2
+#define AM13_CMD_ERASE_SECTORS 3
+
+/* Parameter block layout (matches struct flash_params in loader) */
+struct am13_algo_params {
+ uint8_t dest[4];
+ uint8_t len[4];
+ uint8_t cmd[4];
+ uint8_t status[4];
+ uint8_t buf_addr[4];
+};
+
+/* Flash loader algorithm binary */
+static const uint8_t am13_algo[] = {
+#include "../../../contrib/loaders/flash/am13e230x/am13e230x_algo.inc"
+};
+
+struct am13_flash_bank {
+ uint32_t did;
+ uint32_t traceid;
+ unsigned int main_flash_size_kb;
+ unsigned int main_flash_num_banks;
+ unsigned int sram_size_kb;
+ unsigned int sector_size;
+
+ /* Algorithm state */
+ struct working_area *working_area;
+ struct armv7m_algorithm armv7m_info;
+};
+
+static int am13_auto_probe(struct flash_bank *bank);
+
+/*
+ * GSC flash semaphore must be held before any flash operation.
+ * The debug probe and the CPU have separate ownership tracking
+ * (FLSEMSTAT.DBGACC), so the erase path (debug probe) and the
+ * write path (on-device algorithm) each acquire it independently.
+ */
+
+static int am13_request_gsc_semaphore(struct flash_bank *bank)
+{
+ int retval = target_write_u32(bank->target, GSC_REG_FPC_FLSEMREQ, 1);
+ if (retval != ERROR_OK)
+ LOG_ERROR("AM13: failed to acquire GSC flash semaphore");
+ return retval;
+}
+
+static int am13_clear_gsc_semaphore(struct flash_bank *bank)
+{
+ int retval = target_write_u32(bank->target, GSC_REG_FPC_FLSEMCLR, 1);
+ if (retval != ERROR_OK)
+ LOG_ERROR("AM13: failed to release GSC flash semaphore");
+ return retval;
+}
+
+/* ---------- Flash controller helpers (erase path only) ---------- */
+
+static const struct {
+ unsigned char bit;
+ const char *name;
+} am13_fctl_errors[] = {
+ { 2, "CMDINPROGRESS" },
+ { 4, "FAILWEPROT" },
+ { 5, "FAILVERIFY" },
+ { 6, "FAILILLADDR" },
+ { 7, "FAILMODE" },
+ { 12, "FAILMISC" },
+};
+
+static const char *am13_fctl_strerror(uint32_t status)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(am13_fctl_errors); i++) {
+ if (status & BIT(am13_fctl_errors[i].bit))
+ return am13_fctl_errors[i].name;
+ }
+ return "FAILUNKNOWN";
+}
+
+static int am13_fctl_wait_done(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ uint32_t status = 0;
+ int64_t start_ms = timeval_ms();
+ bool timed_out = false;
+
+ while ((status & FCTL_STATCMD_CMDDONE) == 0) {
+ int retval = target_read_u32(target, FCTL_REG_STATCMD, &status);
+ if (retval != ERROR_OK)
+ return retval;
+ if (timeval_ms() - start_ms > AM13_FLASH_TIMEOUT_MS) {
+ timed_out = true;
+ break;
+ }
+ keep_alive();
+ }
+
+ if ((status & FCTL_STATCMD_CMDPASS) == 0) {
+ if (timed_out)
+ LOG_ERROR("AM13: flash command timed out after %d ms", AM13_FLASH_TIMEOUT_MS);
+ else
+ LOG_ERROR("AM13: flash command failed: %s", am13_fctl_strerror(status));
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static int am13_fctl_clear_status(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ uint32_t status;
+ int64_t start_ms;
+ int retval;
+
+ retval = target_write_u32(target, FCTL_REG_CMDTYPE, FCTL_CMDTYPE_CLEARSTATUS);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_write_u32(target, FCTL_REG_CMDEXEC, FCTL_CMDEXEC_EXECUTE);
+ if (retval != ERROR_OK)
+ return retval;
+
+ start_ms = timeval_ms();
+ do {
+ retval = target_read_u32(target, FCTL_REG_STATCMD, &status);
+ if (retval != ERROR_OK)
+ return retval;
+ if (timeval_ms() - start_ms > AM13_FLASH_TIMEOUT_MS) {
+ LOG_ERROR("Timeout waiting for clear status");
+ return ERROR_FAIL;
+ }
+ keep_alive();
+ } while (status & FCTL_STATCMD_CMDINPROGRESS);
+
+ return ERROR_OK;
+}
+
+static int am13_fctl_unprotect(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ int retval;
+
+ switch (bank->base) {
+ case AM13_FLASH_BASE_MAIN:
+ retval = target_write_u32(target, FCTL_REG_CMDWEPROTA, 0);
+ if (retval != ERROR_OK)
+ return retval;
+ return target_write_u32(target, FCTL_REG_CMDWEPROTB, 0);
+ case AM13_FLASH_BASE_NONMAIN:
+ return target_write_u32(target, FCTL_REG_CMDWEPROTNM, 0);
+ default:
+ return ERROR_FLASH_BANK_INVALID;
+ }
+}
+
+static int am13_fctl_erase(struct flash_bank *bank, uint32_t addr, uint32_t cmd_size)
+{
+ struct target *target = bank->target;
+ int retval;
+
+ retval = am13_fctl_clear_status(bank);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = am13_fctl_unprotect(bank);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_write_u32(target, FCTL_REG_CMDTYPE,
+ FCTL_CMDTYPE_ERASE | cmd_size);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_write_u32(target, FCTL_REG_CMDADDR, addr & 0xFFFFFFF0);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_write_u32(target, FCTL_REG_CMDEXEC, FCTL_CMDEXEC_EXECUTE);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return am13_fctl_wait_done(bank);
+}
+
+/* ---------- Flash driver callbacks ---------- */
+
+FLASH_BANK_COMMAND_HANDLER(am13_flash_bank_command)
+{
+ switch (bank->base) {
+ case AM13_FLASH_BASE_MAIN:
+ case AM13_FLASH_BASE_NONMAIN:
+ break;
+ default:
+ LOG_ERROR("Invalid bank address " TARGET_ADDR_FMT, bank->base);
+ return ERROR_FAIL;
+ }
+
+ struct am13_flash_bank *am13_info = calloc(1, sizeof(*am13_info));
+ if (!am13_info)
+ return ERROR_FAIL;
+
+ bank->driver_priv = am13_info;
+ am13_info->sector_size = AM13_SECTOR_SIZE_BYTES;
+
+ bank->write_start_alignment = AM13_FLASH_WORD_SIZE;
+ bank->write_end_alignment = AM13_FLASH_WORD_SIZE;
+
+ return ERROR_OK;
+}
+
+static int am13_erase(struct flash_bank *bank, unsigned int first, unsigned int last)
+{
+ struct am13_flash_bank *am13_info = bank->driver_priv;
+ int retval;
+
+ if (bank->target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ retval = am13_request_gsc_semaphore(bank);
+ if (retval != ERROR_OK)
+ return retval;
+
+ for (unsigned int s = first; s <= last; s++) {
+ if (bank->sectors[s].is_protected) {
+ LOG_WARNING("AM13: NONMAIN sector %u (0x%08" TARGET_PRIxADDR
+ ") is factory-protected, skipping",
+ s, bank->base + s * am13_info->sector_size);
+ continue;
+ }
+
+ uint32_t addr = bank->base + s * am13_info->sector_size;
+
+ retval = am13_fctl_erase(bank, addr, FCTL_CMDTYPE_SIZE_SECTOR);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("AM13: Sector erase failed at 0x%08" PRIx32 ", returned %d", addr, retval);
+ am13_clear_gsc_semaphore(bank);
+ return retval;
+ }
+ }
+
+ return am13_clear_gsc_semaphore(bank);
+}
+
+/* ---------- Direct write fallback (via debug probe) ---------- */
+
+/*
+ * Word-by-word flash write using direct register access from the debug
+ * probe. Slower than the on-device algorithm but does not require SRAM,
+ * so it works as a fallback when the algorithm cannot be loaded (e.g.,
+ * SRAM is needed by a RAM-resident application).
+ */
+static int am13_write_direct(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ if (count == 0)
+ return ERROR_OK;
+
+ struct target *target = bank->target;
+ uint32_t addr = bank->base + offset;
+ int retval = 0;
+
+ retval = am13_request_gsc_semaphore(bank);
+ if (retval != ERROR_OK)
+ return retval;
+
+ while (count > 0) {
+ /* Clear status + unprotect before every word (per SDK) */
+ retval = am13_fctl_clear_status(bank);
+ if (retval != ERROR_OK)
+ break;
+
+ retval = am13_fctl_unprotect(bank);
+ if (retval != ERROR_OK)
+ break;
+
+ retval = target_write_u32(target, FCTL_REG_CMDTYPE,
+ FCTL_CMDTYPE_PROGRAM | FCTL_CMDTYPE_SIZE_ONEWORD);
+ if (retval != ERROR_OK)
+ break;
+
+ /* All 16 data bytes + both ECC chunks (bits 16-17) enabled */
+ retval = target_write_u32(target, FCTL_REG_CMDBYTEN, 0x0003FFFF);
+ if (retval != ERROR_OK)
+ break;
+
+ retval = target_write_u32(target, FCTL_REG_CMDADDR, addr);
+ if (retval != ERROR_OK)
+ break;
+
+ retval = target_write_u32(target, FCTL_REG_CMDDATAINDEX,
+ (addr >> 4) & 3);
+ if (retval != ERROR_OK)
+ break;
+
+ retval = target_write_buffer(target, FCTL_REG_CMDDATA0, AM13_FLASH_WORD_SIZE, buffer);
+ if (retval != ERROR_OK)
+ break;
+
+ retval = target_write_u32(target, FCTL_REG_CMDEXEC,
+ FCTL_CMDEXEC_EXECUTE);
+ if (retval != ERROR_OK)
+ break;
+
+ retval = am13_fctl_wait_done(bank);
+ if (retval != ERROR_OK)
+ break;
+
+ addr += AM13_FLASH_WORD_SIZE;
+ buffer += AM13_FLASH_WORD_SIZE;
+ count -= AM13_FLASH_WORD_SIZE;
+ }
+
+ int sem_ret = am13_clear_gsc_semaphore(bank);
+ return (retval != ERROR_OK) ? retval : sem_ret;
+}
+
+/* ---------- Flash loader algorithm ---------- */
+
+static int am13_algo_init(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct am13_flash_bank *am13_info = bank->driver_priv;
+ int retval;
+
+ /* Allocate SRAM for algorithm + buffers */
+ target_free_working_area(target, am13_info->working_area);
+ am13_info->working_area = NULL;
+
+ retval = target_alloc_working_area(target, AM13_ALGO_WORKING_SIZE,
+ &am13_info->working_area);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("AM13: insufficient SRAM for flash loader (%u bytes)",
+ AM13_ALGO_WORKING_SIZE);
+ return retval;
+ }
+
+ /*
+ * The flash loader is not position-independent: it references
+ * parameter blocks (AM13_ALGO_PARAMS_0/1) and data buffers
+ * (AM13_ALGO_BUFFER_0/1) at absolute SRAM addresses assembled
+ * into the binary. The working area must start exactly at
+ * AM13_ALGO_BASE (0x20000000).
+ */
+ if (am13_info->working_area->address != AM13_ALGO_BASE) {
+ LOG_ERROR("AM13: working area allocated at 0x%08" TARGET_PRIxADDR
+ ", need 0x%08x; set '_WORKAREABASE 0x%08x' in target config",
+ am13_info->working_area->address,
+ AM13_ALGO_BASE, AM13_ALGO_BASE);
+ target_free_working_area(target, am13_info->working_area);
+ am13_info->working_area = NULL;
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ retval = target_write_buffer(target, AM13_ALGO_BASE, sizeof(am13_algo), am13_algo);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("AM13: failed to load flash algorithm");
+ target_free_working_area(target, am13_info->working_area);
+ am13_info->working_area = NULL;
+ return retval;
+ }
+
+ /*
+ * Release any debug-held GSC semaphore so the algorithm (CPU)
+ * can acquire it â the flash controller tracks ownership via
+ * FLSEMSTAT.DBGACC and rejects mismatched access.
+ */
+ retval = am13_clear_gsc_semaphore(bank);
+ if (retval != ERROR_OK) {
+ target_free_working_area(target, am13_info->working_area);
+ am13_info->working_area = NULL;
+ return retval;
+ }
+
+ am13_info->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+ am13_info->armv7m_info.core_mode = ARM_MODE_THREAD;
+
+ LOG_DEBUG("AM13: starting flash algorithm");
+ retval = target_start_algorithm(target, 0, NULL, 0, NULL,
+ AM13_ALGO_BASE, 0,
+ &am13_info->armv7m_info);
+
+ if (retval != ERROR_OK) {
+ LOG_ERROR("AM13: failed to start flash algorithm");
+ goto err_free_working_area;
+ }
+
+ return ERROR_OK;
+
+err_free_working_area:
+ target_free_working_area(target, am13_info->working_area);
+ am13_info->working_area = NULL;
+ return retval;
+}
+
+static int am13_algo_quit(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct am13_flash_bank *am13_info = bank->driver_priv;
+
+ (void)target_halt(target);
+
+ int retval = target_wait_algorithm(target, 0, NULL, 0, NULL,
+ 0, AM13_FLASH_TIMEOUT_MS,
+ &am13_info->armv7m_info);
+
+ int sem_ret = am13_clear_gsc_semaphore(bank);
+
+ target_free_working_area(target, am13_info->working_area);
+ am13_info->working_area = NULL;
+
+ return (retval != ERROR_OK) ? retval : sem_ret;
+}
+
+static int am13_algo_wait_done(struct flash_bank *bank, uint32_t params_addr)
+{
+ struct target *target = bank->target;
+ uint32_t status = AM13_BUFFER_FULL;
+ int64_t start_ms = timeval_ms();
+ int retval;
+ bool timed_out = false;
+
+ while (status == AM13_BUFFER_FULL) {
+ retval = target_read_u32(target, params_addr + AM13_STATUS_OFFSET,
+ &status);
+ if (retval != ERROR_OK)
+ return retval;
+
+ int64_t elapsed_ms = timeval_ms() - start_ms;
+ if (elapsed_ms > 500)
+ keep_alive();
+ if (elapsed_ms > AM13_FLASH_TIMEOUT_MS) {
+ timed_out = true;
+ break;
+ }
+ }
+
+ if (timed_out) {
+ LOG_ERROR("AM13: flash algorithm timed out after %d ms", AM13_FLASH_TIMEOUT_MS);
+ return ERROR_FAIL;
+ }
+
+ if (status != AM13_BUFFER_EMPTY) {
+ LOG_ERROR("AM13: flash algorithm error, status=0x%08" PRIx32,
+ status);
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static int am13_write(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ struct target *target = bank->target;
+ struct am13_algo_params algo_params[2];
+ int retval;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ retval = am13_algo_init(bank);
+ if (retval != ERROR_OK) {
+ LOG_INFO("AM13: algorithm unavailable, falling back to direct writes");
+ return am13_write_direct(bank, buffer, offset, count);
+ }
+
+ static const uint32_t params_addr[] = {
+ AM13_ALGO_PARAMS_0, AM13_ALGO_PARAMS_1
+ };
+ static const uint32_t buffer_addr[] = {
+ AM13_ALGO_BUFFER_0, AM13_ALGO_BUFFER_1
+ };
+
+ uint32_t address = bank->base + offset;
+ uint32_t index = 0;
+ int64_t start_ms = timeval_ms();
+
+ buf_set_u32(algo_params[0].cmd, 0, 32, AM13_CMD_PROGRAM);
+ buf_set_u32(algo_params[1].cmd, 0, 32, AM13_CMD_PROGRAM);
+
+ while (count > 0) {
+ uint32_t size = MIN(count, AM13_SECTOR_SIZE_BYTES - (address % AM13_SECTOR_SIZE_BYTES));
+
+ retval = target_write_buffer(target, buffer_addr[index], size, buffer);
+ if (retval != ERROR_OK)
+ break;
+
+ buf_set_u32(algo_params[index].dest, 0, 32, address);
+ buf_set_u32(algo_params[index].len, 0, 32, size);
+ buf_set_u32(algo_params[index].status, 0, 32, AM13_BUFFER_FULL);
+
+ /* Write dest/len/cmd/status only â preserve algorithm's buf_addr */
+ retval = target_write_buffer(target, params_addr[index],
+ offsetof(struct am13_algo_params, buf_addr),
+ (uint8_t *)&algo_params[index]);
+ if (retval != ERROR_OK)
+ break;
+
+ index ^= 1;
+ retval = am13_algo_wait_done(bank, params_addr[index]);
+ if (retval != ERROR_OK)
+ break;
+
+ count -= size;
+ buffer += size;
+ address += size;
+
+ if (timeval_ms() - start_ms > 500)
+ keep_alive();
+ }
+
+ /* Wait for the last submitted buffer */
+ if (retval == ERROR_OK) {
+ index ^= 1;
+ retval = am13_algo_wait_done(bank, params_addr[index]);
+ }
+
+ int quit_ret = am13_algo_quit(bank);
+ return (retval != ERROR_OK) ? retval : quit_ret;
+}
+
+/* ---------- Probe ---------- */
+
+static int am13_probe(struct flash_bank *bank)
+{
+ struct target *target = bank->target;
+ struct am13_flash_bank *am13_info = bank->driver_priv;
+ uint32_t did, sramflash;
+ int retval;
+
+ retval = target_read_u32(target, AM13_DID, &did);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (((did & GENMASK(11, 1)) >> 1) != TI_MANUFACTURER_ID) {
+ LOG_ERROR("AM13: unexpected manufacturer ID in DID=0x%08" PRIx32,
+ did);
+ return ERROR_FAIL;
+ }
+
+ am13_info->did = did;
+
+ retval = target_read_u32(target, AM13_TRACEID, &am13_info->traceid);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = target_read_u32(target, AM13_SRAMFLASH, &sramflash);
+ if (retval != ERROR_OK)
+ return retval;
+
+ am13_info->main_flash_size_kb = sramflash & GENMASK(11, 0);
+ am13_info->main_flash_num_banks = (sramflash & GENMASK(13, 12)) >> 12;
+ am13_info->sram_size_kb = (sramflash & GENMASK(25, 16)) >> 16;
+
+ free(bank->sectors);
+ bank->sectors = NULL;
+
+ unsigned int num_sectors;
+ switch (bank->base) {
+ case AM13_FLASH_BASE_MAIN:
+ bank->size = am13_info->main_flash_size_kb * 1024;
+ num_sectors = bank->size / am13_info->sector_size;
+ break;
+ case AM13_FLASH_BASE_NONMAIN:
+ bank->size = am13_info->main_flash_num_banks * AM13_NONMAIN_SIZE_PER_BANK;
+ num_sectors = bank->size / am13_info->sector_size;
+ break;
+ default:
+ LOG_ERROR("Invalid bank address " TARGET_ADDR_FMT, bank->base);
+ return ERROR_FAIL;
+ }
+
+ if (num_sectors == 0) {
+ bank->num_sectors = 0;
+ bank->size = 0;
+ return ERROR_OK;
+ }
+
+ bank->num_sectors = num_sectors;
+ bank->sectors = calloc(num_sectors, sizeof(struct flash_sector));
+ if (!bank->sectors)
+ return ERROR_FAIL;
+
+ /*
+ * NONMAIN sector 0 of each bank contains factory trim and boot
+ * configuration records that are hardware-permanently protected.
+ * The NVMNW returns FAILILLADDR for any erase attempt on them.
+ */
+ unsigned int sectors_per_nm_bank =
+ (bank->base == AM13_FLASH_BASE_NONMAIN)
+ ? AM13_NONMAIN_SIZE_PER_BANK / am13_info->sector_size
+ : 0;
+
+ for (unsigned int i = 0; i < num_sectors; i++) {
+ bank->sectors[i].offset = i * am13_info->sector_size;
+ bank->sectors[i].size = am13_info->sector_size;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected =
+ (sectors_per_nm_bank && (i % sectors_per_nm_bank) == 0) ? 1 : 0;
+ }
+
+ LOG_INFO("AM13: %s flash: %u KB (%u sectors), %u bank(s), SRAM: %u KB",
+ (bank->base == AM13_FLASH_BASE_MAIN) ? "MAIN" : "NONMAIN",
+ (unsigned int)(bank->size / 1024), num_sectors,
+ am13_info->main_flash_num_banks,
+ am13_info->sram_size_kb);
+
+ return ERROR_OK;
+}
+
+static int am13_auto_probe(struct flash_bank *bank)
+{
+ struct am13_flash_bank *am13_info = bank->driver_priv;
+
+ if (am13_info->did)
+ return ERROR_OK;
+
+ return am13_probe(bank);
+}
+
+static int get_am13_info(struct flash_bank *bank,
+ struct command_invocation *cmd)
+{
+ struct am13_flash_bank *am13_info = bank->driver_priv;
+
+ command_print_sameline(cmd, "AM13E23X: DID=0x%08" PRIx32
+ " TRACEID=0x%08" PRIx32
+ " banks=%u",
+ am13_info->did, am13_info->traceid,
+ am13_info->main_flash_num_banks);
+ return ERROR_OK;
+}
+
+const struct flash_driver am13_flash = {
+ .name = "am13",
+ .flash_bank_command = am13_flash_bank_command,
+ .erase = am13_erase,
+ .protect = NULL,
+ .write = am13_write,
+ .read = default_flash_read,
+ .probe = am13_probe,
+ .auto_probe = am13_auto_probe,
+ .erase_check = default_flash_blank_check,
+ .protect_check = NULL,
+ .info = get_am13_info,
+ .free_driver_priv = default_flash_free_driver_priv,
+};
diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h
index 36bba50b3..6f66f7f0a 100644
--- a/src/flash/nor/driver.h
+++ b/src/flash/nor/driver.h
@@ -240,6 +240,7 @@ const struct flash_driver *flash_driver_find_by_name(const char *name);
// Keep in alphabetic order this list of drivers
extern const struct flash_driver aduc702x_flash;
extern const struct flash_driver aducm360_flash;
+extern const struct flash_driver am13_flash;
extern const struct flash_driver ambiqmicro_flash;
extern const struct flash_driver artery_flash;
extern const struct flash_driver at91sam3_flash;
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 03d829da6..2183c4c96 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -19,6 +19,7 @@ static const struct flash_driver * const flash_drivers[] = {
// Keep in alphabetic order the list of drivers
&aduc702x_flash,
&aducm360_flash,
+ &am13_flash,
&ambiqmicro_flash,
&artery_flash,
&at91sam3_flash,
commit e975d571bc5ebd703700ef105b11e6d60768a630
Author: Sameer Srivastava <s-s...@ti...>
Date: Sun Mar 8 12:12:04 2026 +0530
contrib/loaders/flash/am13e230x: add flash loader algorithm
Add a flash loader for AM13E230X that receives flash sectors via shared
memory ping-pong buffers, similar to the CC26xx flash loader.
The flash loader am13e230x_algo.inc was built with the Arm GNU Toolchain
15.2.1 20251203.
Change-Id: If5759b6f513cf94a0208d1bf4e95eb012a297d17
Signed-off-by: Sameer Srivastava <s-s...@ti...>
Reviewed-on: https://review.openocd.org/c/openocd/+/9526
Tested-by: jenkins
Reviewed-by: Nishanth Menon <nm...@ti...>
Reviewed-by: Tomas Vanek <va...@fb...>
diff --git a/contrib/loaders/flash/am13e230x/Makefile b/contrib/loaders/flash/am13e230x/Makefile
new file mode 100644
index 000000000..689e4cdf8
--- /dev/null
+++ b/contrib/loaders/flash/am13e230x/Makefile
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+
+BIN2C = ../../../../src/helper/bin2char.sh
+
+CROSS_COMPILE ?= arm-none-eabi-
+GCC = $(CROSS_COMPILE)gcc
+OBJCOPY = $(CROSS_COMPILE)objcopy
+
+FLAGS = -mcpu=cortex-m33 -mthumb -Os
+FLAGS += -ffunction-sections -fdata-sections
+FLAGS += -g -gdwarf-3 -gstrict-dwarf
+FLAGS += -Wall -fno-strict-aliasing -fno-builtin
+FLAGS += -nostdlib -nostartfiles
+
+CFLAGS = -c -I.
+
+OBJS := \
+ startup.o \
+ main.o \
+ flash.o
+
+all: am13e230x_algo.inc
+
+%.o: %.c
+ @echo 'Building: $<'
+ $(GCC) $(FLAGS) $(CFLAGS) -o "$@" "$<"
+
+am13e230x_algo.out: $(OBJS)
+ @echo 'Linking: $@'
+ $(GCC) $(FLAGS) -o $@ $(OBJS) -Wl,-T"am13e230x.lds"
+
+%.bin: %.out
+ @echo 'Objcopy: $@'
+ $(OBJCOPY) -Obinary $< $@
+
+%.inc: %.bin
+ @echo 'Bin2Char: $@'
+ $(BIN2C) < $< > $@
+ rm $< $*.out
+
+clean:
+ rm -rf *.o *.d *.out *.bin *.inc *.map
+
+.PRECIOUS: %.bin
+
+.PHONY: all clean
diff --git a/contrib/loaders/flash/am13e230x/am13e230x.lds b/contrib/loaders/flash/am13e230x/am13e230x.lds
new file mode 100644
index 000000000..01240c11b
--- /dev/null
+++ b/contrib/loaders/flash/am13e230x/am13e230x.lds
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/******************************************************************************
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * Linker script for AM13E230X flash loader algorithm.
+ * Everything runs from SRAM. Params and buffers are at fixed addresses
+ * that the OpenOCD driver knows about (see flash.h).
+ *
+ *****************************************************************************/
+
+ENTRY( entry )
+
+MEMORY
+{
+ /* Code, data, bss, stack */
+ PROGRAM (RWX) : ORIGIN = 0x20000000, LENGTH = 0x2000
+ /* Params and data buffers at known addresses */
+ BUFFERS (RWX) : ORIGIN = 0x20002000, LENGTH = 0x1100
+}
+
+SECTIONS
+{
+ .text :
+ {
+ _text = .;
+ *(.entry*)
+ *(.text*)
+ _etext = .;
+ } > PROGRAM
+
+ .data :
+ {
+ _data = .;
+ *(.rodata*)
+ *(.data*)
+ _edata = .;
+ } > PROGRAM
+
+ .bss (NOLOAD) :
+ {
+ __bss_start__ = .;
+ _bss = .;
+ *(.bss*)
+ *(COMMON)
+ _ebss = .;
+ __bss_end__ = .;
+ } > PROGRAM
+
+ .stack (NOLOAD) :
+ {
+ _stack = .;
+ *(.stack*)
+ _estack = .;
+ } > PROGRAM
+
+ .buffers (NOLOAD) :
+ {
+ _buffers = .;
+ *(.buffers.params)
+ . = 0x100;
+ *(.buffers.buf0)
+ *(.buffers.buf1)
+ _ebuffers = .;
+ } > BUFFERS
+}
diff --git a/contrib/loaders/flash/am13e230x/am13e230x_algo.inc b/contrib/loaders/flash/am13e230x/am13e230x_algo.inc
new file mode 100644
index 000000000..30f710f04
--- /dev/null
+++ b/contrib/loaders/flash/am13e230x/am13e230x_algo.inc
@@ -0,0 +1,49 @@
+/* Autogenerated with ../../../../src/helper/bin2char.sh */
+0x4f,0xf0,0x00,0x00,0x80,0xf3,0x14,0x88,0xbf,0xf3,0x6f,0x8f,0xdf,0xf8,0x18,0xd0,
+0x06,0x48,0x07,0x49,0x4f,0xf0,0x00,0x02,0x88,0x42,0xb8,0xbf,0x40,0xf8,0x04,0x2b,
+0xfa,0xdb,0x00,0xf0,0x07,0xf8,0xfe,0xe7,0x88,0x04,0x00,0x20,0xf8,0x02,0x00,0x20,
+0xf8,0x02,0x00,0x20,0x2d,0xe9,0xf8,0x43,0x00,0x25,0x41,0x4c,0x41,0x4b,0x23,0x61,
+0x41,0x4b,0x63,0x62,0x23,0x7b,0x25,0x73,0x63,0x7b,0x65,0x73,0xa3,0x7b,0xa5,0x73,
+0xe3,0x7b,0xe5,0x73,0x94,0xf8,0x20,0x30,0x84,0xf8,0x20,0x50,0x94,0xf8,0x21,0x30,
+0x84,0xf8,0x21,0x50,0x94,0xf8,0x22,0x30,0x84,0xf8,0x22,0x50,0x94,0xf8,0x23,0x30,
+0x84,0xf8,0x23,0x50,0x00,0xf0,0xac,0xf8,0x50,0xb1,0x23,0x7b,0x04,0x23,0x23,0x73,
+0x63,0x7b,0x01,0x23,0x63,0x73,0xa3,0x7b,0xa5,0x73,0xe3,0x7b,0xe5,0x73,0xfe,0xe7,
+0x05,0x46,0x4f,0xf0,0x14,0x09,0x80,0x46,0x09,0xfb,0x05,0xf6,0xa7,0x19,0x07,0xf1,
+0x08,0x02,0x53,0x68,0x00,0x2b,0xfc,0xd0,0xbb,0x68,0x02,0x2b,0x19,0xd0,0x03,0x2b,
+0x24,0xd0,0x01,0x2b,0x19,0xd0,0x40,0xf2,0x03,0x12,0x14,0x23,0x03,0xfb,0x05,0x44,
+0xe2,0x60,0x08,0x34,0x00,0xf0,0x9c,0xf8,0x50,0xb1,0x05,0x22,0x23,0x79,0x22,0x71,
+0x62,0x79,0x01,0x22,0x00,0x23,0x62,0x71,0xa2,0x79,0xa3,0x71,0xe2,0x79,0xe3,0x71,
+0xfe,0xe7,0xa0,0x59,0x00,0xf0,0x9a,0xf8,0x68,0xb9,0x38,0x69,0xa1,0x59,0x7a,0x68,
+0x00,0xf0,0xb0,0xf8,0x98,0xb1,0x4f,0xf4,0x81,0x72,0xde,0xe7,0xa6,0x59,0x7b,0x68,
+0xf2,0x18,0x17,0x46,0x09,0xd3,0x40,0xf2,0x01,0x12,0xd6,0xe7,0x30,0x46,0x00,0xf0,
+0x85,0xf8,0x00,0x28,0xf7,0xd1,0x06,0xf5,0x00,0x66,0xb7,0x42,0xf6,0xd8,0x09,0xfb,
+0x05,0x43,0x1a,0x7b,0x83,0xf8,0x0c,0x80,0x5a,0x7b,0x83,0xf8,0x0d,0x80,0x9a,0x7b,
+0x83,0xf8,0x0e,0x80,0xda,0x7b,0x85,0xf0,0x01,0x05,0x83,0xf8,0x0f,0x80,0xab,0xe7,
+0x00,0x20,0x00,0x20,0x00,0x21,0x00,0x20,0x00,0x29,0x00,0x20,0xfe,0xe7,0x00,0x00,
+0xbf,0xf3,0x4f,0x8f,0xbf,0xf3,0x6f,0x8f,0x01,0x23,0x0d,0x49,0xc1,0xf8,0x00,0x31,
+0xbf,0xf3,0x4f,0x8f,0xbf,0xf3,0x6f,0x8f,0x4f,0xf4,0x00,0x03,0x01,0x3b,0xd1,0xf8,
+0xd0,0x23,0x09,0xd0,0x12,0xf0,0x01,0x0f,0xf8,0xd0,0x12,0xf0,0x02,0x0f,0x0c,0xbf,
+0x6f,0xf0,0x01,0x00,0x00,0x20,0x70,0x47,0x4f,0xf0,0xff,0x30,0x70,0x47,0x00,0xbf,
+0x00,0x30,0x04,0x40,0x05,0x22,0x0d,0x4b,0xc3,0xf8,0x04,0x21,0xbf,0xf3,0x4f,0x8f,
+0xbf,0xf3,0x6f,0x8f,0x01,0x22,0xc3,0xf8,0x00,0x21,0xbf,0xf3,0x4f,0x8f,0xbf,0xf3,
+0x6f,0x8f,0x4f,0xf4,0x00,0x02,0xd3,0xf8,0xd0,0x03,0x10,0xf0,0x04,0x00,0x00,0xd1,
+0x70,0x47,0x01,0x3a,0xf7,0xd1,0x4f,0xf0,0xff,0x30,0x70,0x47,0x00,0x30,0x04,0x40,
+0x01,0x22,0x0a,0x4b,0xc3,0xf8,0x00,0x28,0xbf,0xf3,0x4f,0x8f,0xbf,0xf3,0x6f,0x8f,
+0xd3,0xf8,0x08,0x08,0x20,0xf0,0xff,0x40,0x20,0xf4,0xfc,0x00,0x00,0x0c,0x00,0x04,
+0xb0,0xf1,0x00,0x40,0x18,0xbf,0x4f,0xf0,0xff,0x30,0x70,0x47,0x00,0x70,0x04,0x40,
+0x01,0x22,0x05,0x4b,0xc3,0xf8,0x04,0x28,0xbf,0xf3,0x4f,0x8f,0xbf,0xf3,0x6f,0x8f,
+0xd3,0xf8,0x08,0x08,0xc0,0x17,0x70,0x47,0x00,0x70,0x04,0x40,0x08,0xb5,0x01,0x46,
+0xff,0xf7,0xb8,0xff,0x88,0xb9,0x42,0x22,0x09,0x4b,0x21,0xf0,0x0f,0x01,0xc3,0xf8,
+0xd0,0x01,0xc3,0xf8,0xd4,0x01,0xc3,0xf8,0x10,0x02,0xc3,0xf8,0x04,0x21,0xc3,0xf8,
+0x20,0x11,0xbd,0xe8,0x08,0x40,0xff,0xf7,0x83,0xbf,0x4f,0xf0,0xff,0x30,0x08,0xbd,
+0x00,0x30,0x04,0x40,0x2d,0xe9,0xf0,0x47,0x80,0x46,0x89,0x46,0x14,0x46,0x01,0x26,
+0x23,0x4d,0x24,0x4f,0x0c,0xb9,0x20,0x46,0x13,0xe0,0x10,0x2c,0xa2,0x46,0x28,0xbf,
+0x4f,0xf0,0x10,0x0a,0x06,0xfa,0x0a,0xf1,0x08,0x2c,0x01,0xf1,0xff,0x31,0x94,0xbf,
+0x41,0xf4,0x80,0x31,0x41,0xf4,0x40,0x31,0xff,0xf7,0x84,0xff,0x18,0xb1,0x4f,0xf0,
+0xff,0x30,0xbd,0xe8,0xf0,0x87,0x42,0x46,0x29,0xf0,0x0f,0x03,0xc5,0xf8,0xd0,0x01,
+0xc5,0xf8,0xd4,0x01,0xc5,0xf8,0x10,0x02,0xc5,0xf8,0x04,0x61,0xc5,0xf8,0x24,0x11,
+0xc5,0xf8,0x20,0x31,0xc9,0xf3,0x01,0x13,0xc5,0xf8,0x2c,0x31,0x93,0x78,0x51,0x78,
+0x1b,0x04,0x43,0xea,0x01,0x23,0x11,0x78,0x04,0x32,0x0b,0x43,0x12,0xf8,0x01,0x1c,
+0x43,0xea,0x01,0x63,0xc3,0x51,0x04,0x30,0x82,0x45,0xef,0xd8,0xff,0xf7,0x38,0xff,
+0x00,0x28,0xd4,0xd1,0xd1,0x44,0xd0,0x44,0xa4,0xeb,0x0a,0x04,0xba,0xe7,0x00,0xbf,
+0x00,0x30,0x04,0x40,0x30,0x31,0x04,0x40,
diff --git a/contrib/loaders/flash/am13e230x/flash.c b/contrib/loaders/flash/am13e230x/flash.c
new file mode 100644
index 000000000..e9d0b1510
--- /dev/null
+++ b/contrib/loaders/flash/am13e230x/flash.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/******************************************************************************
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * Flash operations for AM13E230X, translated from the OpenOCD driver
+ * (src/flash/nor/am13e230x.c) to run on-device with direct register access.
+ *
+ * Memory barriers (__DSB/__ISB) are required around CMDEXEC writes per the
+ * SDK's DL_FlashCTL_executeCommand â the Cortex-M33 write buffer can
+ * reorder peripheral writes without them.
+ *
+ *****************************************************************************/
+
+#include "flash.h"
+
+/*
+ * ARM Cortex-M memory barrier intrinsics.
+ * These are compiler built-ins for DSB and ISB instructions.
+ */
+#define __DSB() __asm volatile("dsb 0xF" ::: "memory")
+#define __ISB() __asm volatile("isb 0xF" ::: "memory")
+
+int flash_request_gsc_semaphore(void)
+{
+ HWREG(GSC_REG_FPC_FLSEMREQ) = 0x00000001;
+ /* Ensure the semaphore request write reaches the peripheral before
+ * reading FLSEMSTAT */
+ __DSB();
+ __ISB();
+ uint32_t stat = HWREG(GSC_REG_FPC_FLSEMSTAT);
+ /* ASSIGNED must be set and DBGACC must be clear (CPU holds it) */
+ if ((stat & (GSC_FLSEMSTAT_ASSIGNED | GSC_FLSEMSTAT_DBGACC)) != GSC_FLSEMSTAT_ASSIGNED)
+ return -1;
+ return 0;
+}
+
+int flash_clear_gsc_semaphore(void)
+{
+ HWREG(GSC_REG_FPC_FLSEMCLR) = 0x00000001;
+ __DSB();
+ __ISB();
+ uint32_t stat = HWREG(GSC_REG_FPC_FLSEMSTAT);
+ /* ASSIGNED must be clear (semaphore released) */
+ if (stat & GSC_FLSEMSTAT_ASSIGNED)
+ return -1;
+ return 0;
+}
+
+/*
+ * Execute a flash command and wait for completion.
+ * Must be called after all command registers (CMDTYPE, CMDADDR, etc.)
+ * are configured. Matches the SDK's DL_FlashCTL_executeCommand.
+ *
+ * Returns 0 on success, -1 on timeout, -2 on command failure.
+ */
+static int flash_execute_cmd(void)
+{
+ /* Ensure all config register writes complete before execute */
+ __DSB();
+ __ISB();
+
+ HWREG(FCTL_REG_CMDEXEC) = FCTL_CMDEXEC_EXECUTE;
+
+ /* Ensure execute write completes before polling status */
+ __DSB();
+ __ISB();
+
+ uint32_t status;
+ uint32_t timeout = FLASH_TIMEOUT;
+
+ do {
+ status = HWREG(FCTL_REG_STATCMD);
+ if (--timeout == 0)
+ return -1;
+ } while ((status & FCTL_STATCMD_CMDDONE) == 0);
+
+ if ((status & FCTL_STATCMD_CMDPASS) == 0)
+ return -2;
+
+ return 0;
+}
+
+/*
+ * Clear the STATCMD register by issuing a CLEARSTATUS command.
+ * Must be called before each erase operation.
+ */
+static int flash_clear_status(void)
+{
+ HWREG(FCTL_REG_CMDTYPE) = FCTL_CMDTYPE_CLEARSTATUS;
+
+ __DSB();
+ __ISB();
+
+ HWREG(FCTL_REG_CMDEXEC) = FCTL_CMDEXEC_EXECUTE;
+
+ __DSB();
+ __ISB();
+
+ uint32_t timeout = FLASH_TIMEOUT;
+ while (HWREG(FCTL_REG_STATCMD) & FCTL_STATCMD_CMDINPROGRESS) {
+ if (--timeout == 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Unprotect all sectors by clearing protection registers.
+ * Protection is automatically re-enabled by hardware after each operation.
+ */
+static void flash_unprotect(void)
+{
+ HWREG(FCTL_REG_CMDWEPROTA) = 0;
+ HWREG(FCTL_REG_CMDWEPROTB) = 0;
+ HWREG(FCTL_REG_CMDWEPROTNM) = 0;
+}
+
+int flash_sector_erase(uint32_t addr)
+{
+ if (flash_clear_status() != 0)
+ return -1;
+
+ flash_unprotect();
+
+ HWREG(FCTL_REG_CMDTYPE) = FCTL_CMDTYPE_ERASE | FCTL_CMDTYPE_SIZE_SECTOR;
+ HWREG(FCTL_REG_CMDADDR) = addr & 0xFFFFFFF0;
+
+ return flash_execute_cmd();
+}
+
+int flash_write_sector(const uint8_t *data, uint32_t addr, uint32_t count)
+{
+ while (count > 0) {
+ uint32_t num_bytes = (count < FLASH_WORD_SIZE) ? count : FLASH_WORD_SIZE;
+ uint32_t num_send = num_bytes;
+ const uint8_t *write_buf = data;
+
+ /* Byte enable: one bit per data byte + ECC chunk bits */
+ uint32_t bytes_en = (1u << num_send) - 1;
+ bytes_en |= (1u << 16); /* ECC chunk 0 */
+ if (num_send > 8)
+ bytes_en |= (1u << 17); /* ECC chunk 1 */
+
+ /* SDK calls executeClearStatus + unprotect before every word write */
+ if (flash_clear_status() != 0)
+ return -1;
+ flash_unprotect();
+
+ HWREG(FCTL_REG_CMDTYPE) = FCTL_CMDTYPE_PROGRAM | FCTL_CMDTYPE_SIZE_ONEWORD;
+ HWREG(FCTL_REG_CMDBYTEN) = bytes_en;
+ HWREG(FCTL_REG_CMDADDR) = addr & 0xFFFFFFF0;
+
+ /* Flash word index: (address >> 4) & 3, per SDK */
+ HWREG(FCTL_REG_CMDDATAINDEX) = (addr >> 4) & 3;
+
+ /* Write data to flash data register (up to 4 x 32-bit words) */
+ volatile uint32_t *data_reg = (volatile uint32_t *)FCTL_REG_CMDDATA0;
+ for (uint32_t i = 0; i < num_send; i += 4) {
+ data_reg[i / 4] = (uint32_t)write_buf[i]
+ | ((uint32_t)write_buf[i + 1] << 8)
+ | ((uint32_t)write_buf[i + 2] << 16)
+ | ((uint32_t)write_buf[i + 3] << 24);
+ }
+
+ if (flash_execute_cmd() != 0)
+ return -1;
+
+ addr += num_bytes;
+ data += num_bytes;
+ count -= num_bytes;
+ }
+
+ return 0;
+}
diff --git a/contrib/loaders/flash/am13e230x/flash.h b/contrib/loaders/flash/am13e230x/flash.h
new file mode 100644
index 000000000..5e026c33f
--- /dev/null
+++ b/contrib/loaders/flash/am13e230x/flash.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/******************************************************************************
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * Flash register definitions and operation prototypes for AM13E230X.
+ * Derived from src/flash/nor/am13e230x.c
+ *
+ *****************************************************************************/
+
+#ifndef OPENOCD_LOADERS_FLASH_AM13E230X_FLASH_H
+#define OPENOCD_LOADERS_FLASH_AM13E230X_FLASH_H
+
+#include <stdint.h>
+
+/* Direct 32-bit register access */
+#define HWREG(x) (*((volatile uint32_t *)(x)))
+
+/*
+ * Flash Controller (FCTL) registers
+ * Base: 0x40042000
+ * Register block offset: 0x1000
+ */
+#define FLASH_CONTROL_BASE 0x40042000
+
+#define FCTL_REG_CMDEXEC (FLASH_CONTROL_BASE + 0x1100)
+#define FCTL_REG_CMDTYPE (FLASH_CONTROL_BASE + 0x1104)
+#define FCTL_REG_CMDADDR (FLASH_CONTROL_BASE + 0x1120)
+#define FCTL_REG_CMDBYTEN (FLASH_CONTROL_BASE + 0x1124)
+#define FCTL_REG_CMDDATAINDEX (FLASH_CONTROL_BASE + 0x112C)
+#define FCTL_REG_CMDDATA0 (FLASH_CONTROL_BASE + 0x1130)
+#define FCTL_REG_CMDWEPROTA (FLASH_CONTROL_BASE + 0x11D0)
+#define FCTL_REG_CMDWEPROTB (FLASH_CONTROL_BASE + 0x11D4)
+#define FCTL_REG_CMDWEPROTNM (FLASH_CONTROL_BASE + 0x1210)
+#define FCTL_REG_STATCMD (FLASH_CONTROL_BASE + 0x13D0)
+
+/* STATCMD bits */
+#define FCTL_STATCMD_CMDDONE 0x00000001
+#define FCTL_STATCMD_CMDPASS 0x00000002
+#define FCTL_STATCMD_CMDINPROGRESS 0x00000004
+
+/* CMDEXEC bits */
+#define FCTL_CMDEXEC_EXECUTE 0x00000001
+
+/* CMDTYPE command field */
+#define FCTL_CMDTYPE_PROGRAM 0x00000001
+#define FCTL_CMDTYPE_ERASE 0x00000002
+#define FCTL_CMDTYPE_CLEARSTATUS 0x00000005
+
+/* CMDTYPE size field */
+#define FCTL_CMDTYPE_SIZE_ONEWORD 0x00000000
+#define FCTL_CMDTYPE_SIZE_SECTOR 0x00000040
+
+/*
+ * GSC (Global System Control) flash semaphore registers
+ */
+#define GSC_BASE 0x40046000
+#define GSC_REG_FPC_FLSEMREQ (GSC_BASE + 0x1800)
+#define GSC_REG_FPC_FLSEMCLR (GSC_BASE + 0x1804)
+#define GSC_REG_FPC_FLSEMSTAT (GSC_BASE + 0x1808)
+
+/* FLSEMSTAT bits */
+#define GSC_FLSEMSTAT_DBGACC 0x00010000 /* 1 = held by debug probe */
+#define GSC_FLSEMSTAT_ASSIGNED 0x80000000 /* 1 = semaphore is held */
+
+/* Flash geometry */
+#define SECTOR_SIZE 0x800 /* 2KB */
+#define FLASH_WORD_SIZE 16 /* 128-bit = 16 bytes */
+
+/* Flash region bases */
+#define FLASH_BASE_MAIN 0x00000000
+
+/* Timeout loop count */
+#define FLASH_TIMEOUT 0x00800000
+
+/*
+ * Flash operation functions
+ */
+
+/* Acquire/release GSC flash semaphore. Return 0 on success, -1 on failure. */
+int flash_request_gsc_semaphore(void);
+int flash_clear_gsc_semaphore(void);
+
+/* Erase a single sector at the given address */
+int flash_sector_erase(uint32_t addr);
+
+/* Write count bytes from data to flash at addr (handles 16-byte word writes) */
+int flash_write_sector(const uint8_t *data, uint32_t addr, uint32_t count);
+
+#endif /* OPENOCD_LOADERS_FLASH_AM13E230X_FLASH_H */
diff --git a/contrib/loaders/flash/am13e230x/main.c b/contrib/loaders/flash/am13e230x/main.c
new file mode 100644
index 000000000..6329d14f3
--- /dev/null
+++ b/contrib/loaders/flash/am13e230x/main.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/******************************************************************************
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * AM13E230X Flash Loader Overview:
+ * --------------------------------
+ * This is a bare-metal flash programming algorithm for the AM13E230X
+ * microcontroller. OpenOCD loads the compiled binary into device SRAM
+ * at 0x20000000 and starts execution. OpenOCD then streams flash data
+ * into two sector-sized (2 KB) SRAM buffers via the debug probe using
+ * a shared-memory handshake protocol identical to the CC26xx loader.
+ *
+ * The algorithm runs an infinite loop, waiting for OpenOCD to fill a buffer
+ * and signal readiness. It then erases the target sector and programs the
+ * buffer contents into flash. Two buffers are used in ping-pong fashion so
+ * that OpenOCD can fill the next buffer while the current one is being
+ * programmed.
+ *
+ * Shared-Memory Protocol
+ * ----------------------
+ * Communication uses two parameter blocks and two data buffers at fixed
+ * SRAM addresses:
+ *
+ * Address Content
+ * ---------- -------
+ * 0x20002000 flash_params[0] (20 bytes)
+ * 0x20002014 flash_params[1] (20 bytes)
+ * 0x20002100 buffer[0] (2048 bytes)
+ * 0x20002900 buffer[1] (2048 bytes)
+ *
+ * struct flash_params {
+ * uint32_t dest; // Flash destination address
+ * uint32_t len; // Number of bytes in buffer
+ * uint32_t cmd; // Command (see below)
+ * uint32_t status; // BUFFER_FULL=0xFFFFFFFF / BUFFER_EMPTY=0x0
+ * uint32_t buf_addr; // Address of associated data buffer
+ * };
+ *
+ * Commands:
+ * 0 CMD_NO_ACTION Do nothing
+ * 1 CMD_PROGRAM Program buffer contents to flash
+ * 2 CMD_ERASE_AND_PROGRAM Erase sector, then program
+ * 3 CMD_ERASE_SECTORS Erase sectors in range [dest, dest+len)
+ *
+ * Sequence (per sector):
+ * 1. OpenOCD writes sector data to buffer[N]
+ * 2. OpenOCD writes dest, len, cmd to params[N]
+ * 3. OpenOCD writes BUFFER_FULL (0xFFFFFFFF) to params[N].status
+ * 4. Algorithm sees BUFFER_FULL, performs erase + program
+ * 5. Algorithm writes BUFFER_EMPTY (0x0) to params[N].status
+ * 6. OpenOCD polls params[N].status, sees EMPTY, proceeds to next
+ * 7. Meanwhile, algorithm moves to params[N^1] (ping-pong)
+ *
+ * On error, the algorithm writes an error code (non-zero, non-0xFFFFFFFF)
+ * to params[N].status and enters an infinite loop.
+ *
+ *****************************************************************************/
+
+#include <stdint.h>
+#include "flash.h"
+
+/* Handshake values */
+#define BUFFER_EMPTY 0x00000000
+#define BUFFER_FULL 0xFFFFFFFF
+
+/* Commands */
+#define CMD_NO_ACTION 0
+#define CMD_PROGRAM 1
+#define CMD_ERASE_AND_PROGRAM 2
+#define CMD_ERASE_SECTORS 3
+
+/* Status codes written on error */
+#define STATUS_OK 0x00000000
+#define STATUS_FAILED_ERASE 0x00000101
+#define STATUS_FAILED_PROGRAM 0x00000102
+#define STATUS_FAILED_UNKNOWN_CMD 0x00000103
+#define STATUS_FAILED_SEM_ACQUIRE 0x00000104
+#define STATUS_FAILED_SEM_RELEASE 0x00000105
+
+/* Parameter block -- must match the OpenOCD driver's struct */
+struct __attribute__((packed)) flash_params {
+ uint32_t dest; /* Flash destination address */
+ uint32_t len; /* Number of bytes */
+ uint32_t cmd; /* Command */
+ uint32_t status; /* Handshake: BUFFER_FULL / BUFFER_EMPTY */
+ uint32_t buf_addr; /* Address of data buffer */
+};
+
+/* Params and buffers placed at fixed addresses by the linker script */
+__attribute__((section(".buffers.params")))
+volatile struct flash_params g_params[2];
+
+__attribute__((section(".buffers.buf0")))
+uint8_t g_buf0[SECTOR_SIZE];
+
+__attribute__((section(".buffers.buf1")))
+uint8_t g_buf1[SECTOR_SIZE];
+
+static int do_erase_and_program(volatile struct flash_params *p)
+{
+ /* Erase the target sector */
+ if (flash_sector_erase(p->dest) != 0)
+ return STATUS_FAILED_ERASE;
+
+ /* Program the buffer contents */
+ if (flash_write_sector((const uint8_t *)p->buf_addr, p->dest, p->len) != 0)
+ return STATUS_FAILED_PROGRAM;
+
+ return STATUS_OK;
+}
+
+static int do_program(volatile struct flash_params *p)
+{
+ if (flash_write_sector((const uint8_t *)p->buf_addr, p->dest, p->len) != 0)
+ return STATUS_FAILED_PROGRAM;
+
+ return STATUS_OK;
+}
+
+static int do_erase_sectors(volatile struct flash_params *p)
+{
+ uint32_t addr = p->dest;
+ uint32_t len = p->len;
+
+ /* Guard against dest+len wrapping past UINT32_MAX */
+ if (len > (UINT32_MAX - addr))
+ return STATUS_FAILED_ERASE;
+
+ uint32_t end = addr + len;
+
+ while (addr < end) {
+ if (flash_sector_erase(addr) != 0)
+ return STATUS_FAILED_ERASE;
+ addr += SECTOR_SIZE;
+ }
+
+ return STATUS_OK;
+}
+
+int main(void)
+{
+ uint32_t curr_buf = 0;
+ uint32_t status;
+
+ /* Initialize buffer pointers so OpenOCD can read them */
+ g_params[0].buf_addr = (uint32_t)g_buf0;
+ g_params[1].buf_addr = (uint32_t)g_buf1;
+ g_params[0].status = BUFFER_EMPTY;
+ g_params[1].status = BUFFER_EMPTY;
+
+ /* Acquire GSC flash semaphore â must be done by the CPU (not the
+ * debugger) because the flash controller checks FLSEMSTAT.DBGACC
+ * to verify the access source matches the semaphore holder. */
+ if (flash_request_gsc_semaphore() != 0) {
+ g_params[0].status = STATUS_FAILED_SEM_ACQUIRE;
+ while (1)
+ ;
+ }
+
+#ifdef ALGO_DRY_RUN
+ /* Dry-run mode: no flash operations, just ACK every command.
+ * Used to verify the shared-memory handshake works. */
+ while (1) {
+ while (g_params[curr_buf].status == BUFFER_EMPTY)
+ ;
+ g_params[curr_buf].status = BUFFER_EMPTY;
+ curr_buf ^= 1;
+ }
+#endif
+
+ while (1) {
+ /* Wait for OpenOCD to signal that buffer is ready */
+ while (g_params[curr_buf].status == BUFFER_EMPTY)
+ ;
+
+ /* Dispatch command */
+ switch (g_params[curr_buf].cmd) {
+ case CMD_ERASE_AND_PROGRAM:
+ status = do_erase_and_program(&g_params[curr_buf]);
+ break;
+ case CMD_PROGRAM:
+ status = do_program(&g_params[curr_buf]);
+ break;
+ case CMD_ERASE_SECTORS:
+ status = do_erase_sectors(&g_params[curr_buf]);
+ break;
+ default:
+ status = STATUS_FAILED_UNKNOWN_CMD;
+ break;
+ }
+
+ if (status != STATUS_OK) {
+ /* Report error and halt */
+ g_params[curr_buf].status = status;
+ if (flash_clear_gsc_semaphore() != 0)
+ g_params[curr_buf].status = STATUS_FAILED_SEM_RELEASE;
+ while (1)
+ ;
+ }
+
+ /* Signal completion */
+ g_params[curr_buf].status = BUFFER_EMPTY;
+
+ /* Swap to the other buffer */
+ curr_buf ^= 1;
+ }
+}
+
+void _exit(int status)
+{
+ (void)status;
+ while (1)
+ ;
+}
diff --git a/contrib/loaders/flash/am13e230x/startup.c b/contrib/loaders/flash/am13e230x/startup.c
new file mode 100644
index 000000000..26e5ced5f
--- /dev/null
+++ b/contrib/loaders/flash/am13e230x/startup.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/******************************************************************************
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * Startup code for AM13E230X flash loader algorithm.
+ * OpenOCD jumps directly to entry() after loading the binary into SRAM.
+ *
+ *****************************************************************************/
+
+#include <stdint.h>
+
+extern int main(void);
+
+/* Stack: 100 words = 400 bytes (matches cc26xx) */
+__attribute__((section(".stack"), used))
+static uint32_t stack[100];
+
+/* BSS boundaries from linker */
+extern uint32_t _bss;
+extern uint32_t _ebss;
+/* Stack top from linker */
+extern uint32_t _estack;
+
+/*
+ * Entry point. OpenOCD sets PC here after loading the binary.
+ */
+__attribute__((section(".entry"), naked))
+void entry(void)
+{
+ /* Switch to privileged Thread mode using MSP.
+ * The previous application (e.g. Zephyr) may have set
+ * CONTROL.nPRIV=1 (unprivileged). Peripheral access
+ * requires privileged mode. */
+ __asm(" mov r0, #0");
+ __asm(" msr CONTROL, r0");
+ __asm(" isb");
+
+ /* Initialize stack pointer to top of .stack section */
+ __asm(" ldr sp, =_estack");
+
+ /* Zero fill the bss segment */
+ __asm(" ldr r0, =_bss\n"
+ " ldr r1, =_ebss\n"
+ " mov r2, #0\n"
+ " .thumb_func\n"
+ " zero_loop:\n"
+ " cmp r0, r1\n"
+ " it lt\n"
+ " strlt r2, [r0], #4\n"
+ " blt zero_loop");
+
+ /* Call the application's entry point */
+ __asm(" bl main");
+
+ /* If we ever return, infinite loop */
+ __asm(" halt_loop:\n"
+ " b halt_loop");
+}
-----------------------------------------------------------------------
Summary of changes:
contrib/loaders/flash/am13e230x/Makefile | 48 ++
contrib/loaders/flash/am13e230x/am13e230x.lds | 67 ++
contrib/loaders/flash/am13e230x/am13e230x_algo.inc | 49 ++
contrib/loaders/flash/am13e230x/flash.c | 177 +++++
contrib/loaders/flash/am13e230x/flash.h | 91 +++
contrib/loaders/flash/am13e230x/main.c | 216 ++++++
contrib/loaders/flash/am13e230x/startup.c | 60 ++
doc/openocd.texi | 64 ++
src/flash/nor/Makefile.am | 1 +
src/flash/nor/am13e230x.c | 758 +++++++++++++++++++++
src/flash/nor/driver.h | 1 +
src/flash/nor/drivers.c | 1 +
12 files changed, 1533 insertions(+)
create mode 100644 contrib/loaders/flash/am13e230x/Makefile
create mode 100644 contrib/loaders/flash/am13e230x/am13e230x.lds
create mode 100644 contrib/loaders/flash/am13e230x/am13e230x_algo.inc
create mode 100644 contrib/loaders/flash/am13e230x/flash.c
create mode 100644 contrib/loaders/flash/am13e230x/flash.h
create mode 100644 contrib/loaders/flash/am13e230x/main.c
create mode 100644 contrib/loaders/flash/am13e230x/startup.c
create mode 100644 src/flash/nor/am13e230x.c
hooks/post-receive
--
Main OpenOCD repository
|