From 974389b643f61bbc7278a9fa56bdc16ae6b9f9f8 Mon Sep 17 00:00:00 2001 From: cumin <13809292481@163.com> Date: Tue, 22 Sep 2026 14:01:59 +0800 Subject: [PATCH] target: fix nRF54LM20A flash algorithm (pyocd/pyOCD#2016) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous nRF54LM20A flash algorithm enabled only CONFIG.WEN and copied words straight into RRAM with no write-buffer configuration and no buffer commit. Every CPU store to RRAM then faulted (HardFault, IPSR=3) — while erase kept working because EraseChip only pokes RRAMC task registers — so `pyocd flash -t nrf54lm20a` faulted at the first programming store, on any chip state (pyocd/pyOCD#2016). This blob programs through the RRAMC write buffer, matching Nordic's own nRF54LM20A flash algorithm (the same one shipped in probe-rs targets): set CONFIG = WEN | WRITEBUFSIZE(32) = 0x2001, copy the page, flush with TASKS_COMMITWRITEBUF, wait READY, clear WEN. Pages are 4 KiB so each program call covers whole 128-bit RRAM codewords (4-byte pages were observed dropping stray bytes, e.g. 2 bytes/flash in an independent OpenOCD verify). The previous blob's flash algorithm was byte-identical to the official one merged in #1889 (shipped in v0.44.0), so this fix lands on top of that and also applies to any fork/pin that carried it. Hardware verified on a Seeed XIAO nRF54LM20A (on-board CMSIS-DAP): - mass-erase + flash on a blank chip: exit 0, 24576 bytes programmed - independent OpenOCD verify_image: zero byte differences - board resets and runs the image Built with arm-zephyr-eabi-gcc -mcpu=cortex-m33 -mthumb -Os from this C: #include #define RRAMC 0x5004E000u #define TASKS_CWB (*(volatile uint32_t *)(RRAMC + 0x008)) #define READY (*(volatile uint32_t *)(RRAMC + 0x400)) #define CONFIG_REG (*(volatile uint32_t *)(RRAMC + 0x500)) #define ERASEALL (*(volatile uint32_t *)(RRAMC + 0x540)) #define CFG (1u | (0x20u << 8)) /* WEN | WRITEBUFSIZE=32 */ static void wait(void){ while((READY & 1u)==0){} } int Init(uint32_t a){ (void)a; wait(); return 0; } int UnInit(void){ return 0; } int EraseChip(void){ CONFIG_REG=1; wait(); ERASEALL=1; wait(); CONFIG_REG=0; return 0; } int EraseSector(uint32_t a){ CONFIG_REG=CFG; wait(); *(volatile uint32_t*)a=0xFFFFFFFFu; TASKS_CWB=1; wait(); CONFIG_REG=0; return 0; } int ProgramPage(uint32_t a, uint32_t sz, const uint32_t *s){ CONFIG_REG=CFG; wait(); for (uint32_t n=sz&~3u; n; n-=4) *(volatile uint32_t*)a++=*s++; TASKS_CWB=1; wait(); CONFIG_REG=0; return 0; } Fixes pyocd/pyOCD#2016. Co-Authored-By: Claude --- pyocd/target/builtin/target_nRF54LM20A.py | 37 ++++++++++++----------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/pyocd/target/builtin/target_nRF54LM20A.py b/pyocd/target/builtin/target_nRF54LM20A.py index f284ac625..71d0b1e69 100644 --- a/pyocd/target/builtin/target_nRF54LM20A.py +++ b/pyocd/target/builtin/target_nRF54LM20A.py @@ -21,35 +21,36 @@ # Flash algorithm as a hex string 'instructions': [ - 0xe7fdbe00, - 0x68134a02, 0xd1fc2b01, 0x46c04770, 0x5004e400, 0x47702000, 0x47702000, 0x2501b570, 0x60254c05, - 0xffeef7ff, 0x601d4b04, 0xffeaf7ff, 0x60202000, 0x46c0bd70, 0x5004e500, 0x5004e540, 0xb5702301, - 0x00044d06, 0xf7ff602b, 0x2301ffdb, 0x6023425b, 0xffd6f7ff, 0x60282000, 0x46c0bd70, 0x5004e500, - 0x2301b5f8, 0x000f0014, 0x00064d0a, 0xf7ff602b, 0x0022ffc7, 0x00bf08bf, 0x1b3619e7, 0x42ba1993, - 0xf7ffd104, 0x2000ffbd, 0xbdf86028, 0x6019ca02, 0x46c0e7f4, 0x5004e500, 0x00000000, 0x00000000 + 0xe7fdbe00, + 0xf8d24a02, 0x07db3400, 0x4770d5fb, 0x5004e000, 0xf7ffb508, 0x2000fff5, 0x2000bd08, 0x00004770, + 0x49072001, 0xf8c1b508, 0xf7ff0500, 0xf8c1ffe9, 0x20000540, 0xffe4f7ff, 0x0500f8c1, 0xbf00bd08, + 0x5004e000, 0xf242b508, 0x49080301, 0x3500f8c1, 0xffd6f7ff, 0x33fff04f, 0x23016003, 0x608b2000, + 0xffcef7ff, 0x0500f8c1, 0xbf00bd08, 0x5004e000, 0xf242b538, 0x46140301, 0xf0214d0c, 0xf8c50103, + 0xf7ff3500, 0x4622ffbd, 0x1b004421, 0xeb02428a, 0xd1070300, 0x20002301, 0xf7ff60ab, 0xf8c5ffb1, + 0xbd380500, 0x4b04f852, 0xe7ef601c, 0x5004e000, ], # Relative function addresses 'pc_init': 0x20000015, - 'pc_unInit': 0x20000019, - 'pc_program_page': 0x20000065, - 'pc_erase_sector': 0x20000041, - 'pc_eraseAll': 0x2000001d, + 'pc_unInit': 0x2000001f, + 'pc_program_page': 0x20000075, + 'pc_erase_sector': 0x20000049, + 'pc_eraseAll': 0x20000025, - 'static_base' : 0x20000000 + 0x00000004 + 0x000000a0, - 'begin_stack' : 0x20000300, - 'page_size' : 0x4, + 'static_base' : 0x200000B8, + 'begin_stack' : 0x20003000, + 'page_size' : 0x1000, 'analyzer_supported' : False, 'analyzer_address' : 0x00000000, - 'page_buffers' : [0x200000b0,0x200000b4], # Enable double buffering + 'page_buffers' : [0x20000100,0x20001100], # 4 KiB double buffers, 128-bit line aligned 'min_program_length' : 0x4, # Relative region addresses and sizes - 'ro_start': 0x4, - 'ro_size': 0xa0, - 'rw_start': 0xa4, + 'ro_start': 0xb4, + 'ro_size': 0x0, + 'rw_start': 0xb4, 'rw_size': 0x0, - 'zi_start': 0xa4, + 'zi_start': 0xb4, 'zi_size': 0x0, # Flash information