Hi all,
First custom board I've designed and I'm hitting a brick wall on bring-up. Hoping someone here with custom RP2350-family bring-up experience can spot what I've missed. I'd really appreciate a sanity check on the schematic too if anyone has the time.
## The board and tooling
It's a custom RP2354 board. SWD, QSPI flash, RUN, and USB_BOOT (QSPI SS) are all broken out. For debug I'm using a Pi Pico 2 W flashed with debugprobe firmware, talking to OpenOCD on a Raspberry Pi host (cmsis-dap.cfg + target/rp2354.cfg). Firmware is built with the pico-sdk, `-DPICO_BOARD=pico2`.
Schematic is attached. Apologies in advance for the GPIO net names on the breakout, they're misleading and already fixed in the next rev.
## What's happening
I flash a trivial blinky-style program over SWD. After reset, nothing happens on the pin I expect to go high (multimeter reads ~0.20 V on it). When I halt the core and read the PC, it's sitting deep inside `xosc_init` from the SDK's runtime init, well before `main()` ever runs. A small RAM log buffer I write to from the very first line of `main()` is completely empty after halt, which confirms `main()` is not being reached.
## Minimal test program
```c
#include "pico/stdlib.h"
#include "hardware/gpio.h"
int main(void) {
gpio_init(19);
gpio_set_dir(19, GPIO_OUT);
gpio_put(19, 1);
gpio_init(20);
gpio_set_dir(20, GPIO_OUT);
gpio_put(20, 0);
while (true) { tight_loop_contents(); }
}
```
CMakeLists:
```cmake
cmake_minimum_required(VERSION 3.13)
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(charlie_demo C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(charlie_demo main.c)
target_link_libraries(charlie_demo pico_stdlib hardware_gpio)
pico_add_extra_outputs(charlie_demo)
pico_enable_stdio_usb(charlie_demo 0)
pico_enable_stdio_uart(charlie_demo 0)
```
Built clean, no warnings:
```bash
cd ~/charlie_demo
rm -rf build && mkdir build && cd build
cmake .. -DPICO_BOARD=pico2
make -j4
```
## OpenOCD halt and register dump
```text
$ openocd -f interface/cmsis-dap.cfg \
-f target/rp2354.cfg \
-c "adapter speed 5000" \
-c "init" \
-c "halt" \
-c "dump_image /tmp/ramlog.bin 0x20000000 1024" \
-c "mdw 0xd0000010" \
-c "mdw 0xd0000030" \
-c "reg pc" \
-c "exit"
Warn : [rp2354.cm0] target was in unknown state when halt was requested
Info : SWD DPIDR 0x4c013477
Warn : [rp2354.cm1] target was in unknown state when halt was requested
Info : [rp2354.cm0] external reset detected
dumped 1024 bytes in 0.004106s (243.546 KiB/s)
0xd0000010: 00000000
0xd0000030: 00000000
pc (/32): 0x10000f66
```
PC at `0x10000f66` is inside the XOSC startup busy-wait. The strange part: when I halt, the `STABLE` bit in `XOSC.STATUS` is actually set, but the core is still spinning in that loop.
## What I've tried so far
1. Cleared the `ISO` bit in `PADS_BANK0` for the GPIOs (pad isolation defaults on, has to be cleared after pad config). This nudged the PC past the previous stuck address, so the suggestion was definitely on the right track.
2. Bumped `XOSC_STARTUP_DELAY_MULTIPLIER` to 128. No change.
3. Verified `IO_BANK0: GPIOx_STATUS` and confirmed I'm not accidentally overriding the output via `GPIOx_CTRL`.
After step 1, the PC moved but it's still in `xosc_init`, just at a slightly different address:
```text
pc (/32): 0x10000d42
0x40048000 (XOSC_BASE): 00fabaa0 81001000 77616b65 00001780 00000000
0x40050000 (PLL_SYS_BASE): 00000001 0000002d 00000000 00077000
0xd0000010 (SIO GPIO_OUT): 00000000
0xd0000030 (SIO GPIO_OE): 00000000
```
Reading those: `XOSC.CTRL = 0x00fabaa0` and `XOSC.STATUS = 0x81001000` (MSB high, so STABLE is set). SIO `GPIO_OUT` and `GPIO_OE` are both still zero, so we are nowhere near the GPIO config code. PLL_SYS looks like it hasn't been kicked either.
## Where I'm stuck
Tomorrow I'll get a scope on XIN to see whether the crystal is actually oscillating. But before I do that, I'd like to ask the assembled wisdom:
1. Has anyone seen this exact failure mode on a custom RP2350-family board, where `XOSC.STATUS` reads STABLE but the SDK still hangs in `xosc_init`? Is there something I'm misreading about that STATUS register?
2. What are the usual culprits on a custom board that produce a hang this early? Things I'm specifically wondering about: load capacitor mis-sizing on XIN/XOUT, supply sequencing or insufficient decoupling on `DVDD` / `VREG_VOUT`, OTP being in an unexpected state from the factory, or the QSPI flash boot not completing cleanly and somehow leaving the chip in a half-initialised state.
3. Any tips on how to distinguish "crystal genuinely not oscillating" from "crystal is fine, something upstream (resets, PSM, regulator) is keeping us stuck" purely from SWD register reads, so I can narrow it down before reaching for the scope?
Happy to post additional register dumps, the linker map, BOM, or the layout if useful. Schematic attached below.
Thanks in advance, any sanity check would be appreciated.