Core 0 (master, cm33_core0) runs correctly. Core 1 (slave, cm33_core1, role M33SLAVE) never appears to execute even the first instruction of its own main(), despite every element of the boot sequence being independently verified correct at the register and memory level, across two separate clean-room project builds (one hand-written register sequence, one using NXP's own MCMGR middleware). Both attempts reach an identical dead end.
Despite all of the above, a diagnostic value written as the literal first line of Core 1's main() (into memory confirmed shared and correctly addressed) never changes from its initial value.
Rebuilt from scratch using MCMGR_Init() / MCMGR_StartCore() / MCMGR_TriggerEvent() instead of hand-written register writes, to rule out a mistake in our own register sequence.
Result: identical outcome. Core 0 blocks forever inside MCMGR_StartCore(). A diagnostic shared-memory value (confirmed identical address in both cores' .map files, in a genuinely shared SRAM bank, .noinit so unaffected by C-runtime init) written as literally the first executable line of Core 1's main() never leaves its initial value of 0.
Given CPUCFG, CPBOOT, and CPUCTRL all read back exactly as the reference implementation (mcmgr_internal_core_api_lpc55s69.c) intends, and the embedded image at the boot address is independently confirmed to be a valid, correctly-linked Cortex-M binary — is there an additional step required to actually start Core 1's instruction fetch on this part that is not captured by these three register writes alone?
Specifically:
Happy to provide full project files, .map files, or a minimal reproduction project on request.
Posting the resolution in case anyone finds this thread later with a similar symptom.
Core 1 was booting and running correctly the entire time, in every version of this project. The problem was never CPUCFG/CPBOOT/CPUCTRL, the vector table, memory placement, or anything else in the boot sequence - it was a bug in my own diagnostic instrumentation that made it look like Core 1 never executed anything.
I was using a small shared-memory struct (placed via __attribute__((section(".noinit.$SRAM4")))) written by Core 1 and read by Core 0, to observe boot progress. This technique only lands in genuinely shared memory if the project's generated linker script actually defines a region named SRAM4. My from-scratch wizard-created projects did define that region, so the technique worked there structurally - but when I later built a version on top of NXP's own working hello_world example project (which uses differently-named regions, Ram1/rpmsg_sh_mem, not SRAM4), the same attribute silently fell through to each core's own private .noinit memory instead of throwing an error. Core 0 and Core 1 were each reading/writing their own separate copy of the "shared" struct, with no compiler or linker warning that anything was wrong.
Net effect: my diagnostic reported 0 (no progress) on every single test across two independent boot implementations (hand-written CPUCFG/CPBOOT/CPUCTRL registers, and NXP's own MCMGR middleware) and multiple project configurations, because the two cores were never actually looking at the same memory address to begin with - regardless of whether Core 1 was really running.
By copying NXP's working hello_world example verbatim and only adding diagnostic writes to the existing, unmodified, proven-working main.c (rather than replacing it), I could visually confirm via the onboard LED that Core 1 was genuinely executing the full sequence - yet the shared-memory counter still read 0. That contradiction (physical proof of execution vs. a diagnostic saying otherwise) was what exposed that the diagnostic itself, not Core 1, was broken. Fixing the section name to match that project's actual region (.noinit.$rpmsg_sh_mem instead of .noinit.$SRAM4) immediately produced correct, incrementing values.
If you're using a .noinit.$
Thanks to everyone who read through the earlier posts - the CPUCFG/CPBOOT/CPUCTRL/vector-table verification work wasn't wasted; confirming all of that was correct is ultimately what forced the investigation toward the diagnostic mechanism itself as the remaining unverified piece.
Since posting, I ran a decisive test that narrows this down significantly.
Imported multicore_examples/hello_world (primary + secondary) for LPCXpresso55S69 fresh, completely unmodified, and debugged it as-is.
Result: it works. The onboard RGB LED blinks at a clean, regular 500ms on/500ms off rate — confirmed by comparing directly against my own project flashed onto the same board (LED off with my project, LED blinking with the unmodified example, LED off again when I re-flash my own project). This is conclusive: dual-core execution is possible on this exact board, probe, and IDE installation.
To isolate whether the problem was in my application code or my project's build/link configuration, I copied the working example's secondary-core main.c, app.h, and hardware_init.c verbatim (byte-for-byte, unmodified) into my own slave project, replacing my own application code entirely.
Result: the LED does not blink. Identical, unmodified NXP code that works in the original example project fails to start Core 1 when built inside my own project's configuration.
The issue is not in application-level code (confirmed twice now - independently in a hand-written register implementation and in an MCMGR-based implementation, and now a third time with literally the example's own source files). It is something specific to how my project's Multicore linker settings / memory region configuration differs from the working example's, despite my best efforts to match it point-for-point:
One structural difference I've found but not yet resolved: the working example's secondary core project has no PROGRAM_FLASH memory region override at all in its .cproject (only two custom RAM-derived regions). My from-scratch wizard-created slave project has three regions including a relocated PROGRAM_FLASH. Attempting to remove that region entirely (to match the working example precisely) crashes MCUXpresso's MCU Settings page (NullPointerException: this.mcuPage is null), suggesting the IDE's tooling doesn't gracefully support a project with no flash region defined - which may itself be relevant to why matching the working example's exact configuration by hand has been difficult.
Is there a specific, documented multicore project configuration (beyond what the wizard's "M33SLAVE" role + Multicore linker settings produce) required to get a from-scratch project into the same working state as the SDK's own hello_world example? Or is there a known limitation/bug in MCUXpresso IDE v25.6.136's project wizard for multicore LPC55S69 projects that the example's project files work around in a way the wizard doesn't replicate?
Happy to share both projects' complete .cproject files for direct comparison if useful.