boards: add Silicon Labs SiWx917-DK2605A (BRD2605A) - #23
Closed
mikeysklar wants to merge 8 commits into
Closed
Conversation
Wi-Fi 6 + Bluetooth LE 5.4 dev kit built on the SiWG917M111MGTBA: Cortex-M4F at 180 MHz plus a separate network processor, 8 MB flash and 8 MB memory-mapped PSRAM. The SoC has no USB device controller, so boards/siwx917_dk2605a.overlay replaces the port's app.overlay, which unconditionally references zephyr_udc0. The console falls back to the Zephyr console on ulpuart, reaching the host over the on-board J-Link VCOM, and file access is via web workflow. This follows the existing nrf54l15dk precedent. CIRCUITPY is carved out of code_partition rather than the upper flash. In common-flash mode the M4 cannot write flash itself; the driver asks the NWP to do it, and the NWP refuses writes in the OTA-swap region, which fails as -EIO on the first block. code_partition is shrunk from 2008K to 1M (the image uses ~700K) and 984K given to circuitpy. The board conf also sets CONFIG_GPIO and a non-zero kernel heap, neither of which the upstream Zephyr board defconfig enables; without them the build fails to link.
Enable ulab for this board (CIRCUITPY_ULAB), which fits comfortably in the 1 MB code partition at ~76% used. Disable the 8 MB PSRAM node. It works as memory - sparse writes across a 64 KB buffer verify correctly - but using it as the CircuitPython heap corrupts objects under sustained allocation, and a loop with no buffer at all is enough to hang the board. Not a timing problem: identical at fast-freq 144 MHz and 33 MHz. Disabling the node keeps it out of the generated ram_bounds[] so the heap falls back to the 319 KB internal SRAM, which is ample and about 20% faster for array work. Worth re-enabling once the root cause is found; 8 MB of Python heap would be exceptional.
The board enabled NET_SOCKETS, NET_IPV4 and NET_DHCPV4 but never CONFIG_NET_TCP, and the siwx91x driver defaults to the native network stack. DHCP is UDP, so association and the DHCP lease both worked and the board looked healthy while every SOCK_STREAM socket failed to open. The web workflow listener never came up and socketpool raised "Out of sockets" from Python. CONFIG_ZVFS_OPEN_MAX also defaulted to 0, so there were no file descriptors for Zephyr to hand out to sockets. With both set, the web workflow serves: PUT returns 201, the bytes land sha256-exact, and /fs/ returns 401 without credentials. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Wi-Fi path prints on nearly every net event, and those printks shared the
UART with the CircuitPython REPL. A single scan emits dozens of
NET_EVENT_WIFI_SCAN_RESULT lines, which interleave with the raw REPL protocol
and corrupt the handshake:
Raw REPL did not acknowledge (got b'\x1b]')
so any harness driving the board over serial is flaky.
Route Zephyr's console to RTT over the existing SWD connection. Nothing else
has to change: supervisor/serial.c builds its console from
DEVICE_DT_GET(DT_CHOSEN(zephyr_console)) and drives it with busio directly,
never going through Zephyr's console subsystem, so the REPL keeps ulpuart and
the J-Link VCOM while printk, the boot banner and driver logs move to RTT.
RTT needs no extra pin and works on the debug link already in use. Note the
control block cannot be auto-detected on this SoC: J-Link scans the usual
Cortex-M RAM at 0x20000000 and this part has RAM at 0x400, so the address has
to be passed explicitly. Read it from the ELF and hand it to J-Link:
arm-none-eabi-nm zephyr.elf | grep _SEGGER_RTT
JLinkExe ... -autoconnect 1 -RTTTelnetPort 19021 # then: RTTStart <addr>
JLinkRTTClientExe
Verified: the full Zephyr console including the boot banner appears on RTT, the
serial side is quiet, and a multi-line raw-REPL exec that previously failed
repeatedly now succeeds first try while a scan is running.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Root cause of the corruption is found and fixed in siwx917/fix-psram-dcache-corruption: an unmaintained 16 KB data cache dedicated to PSRAM, left enabled and half-configured by the bootloader. With that in place the heap moves from the 319 KB internal sram0 to the 8 MB PSRAM. gc.mem_free() goes from 72,144 to 8,265,200 bytes. Depends on siwx917/fix-psram-dcache-corruption. Enabling this node without it reintroduces the corruption. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mikeysklar
added a commit
that referenced
this pull request
Aug 4, 2026
check_suite["workflowRun"] is null for check suites that aren't attached to a workflow run (e.g. one that was deleted, or belongs to an app integration), and the "scheduler" job's Get-last-commit-with-checks step dereferenced it unconditionally: TypeError: 'NoneType' object is not subscriptable That's what was failing CI on PRs #20 and #23 on this fork. Skip check suites with no workflow run instead of crashing on them.
check_suite["workflowRun"] is null for check suites that aren't attached to a workflow run (e.g. one that was deleted, or belongs to an app integration), and the "scheduler" job's Get-last-commit-with-checks step dereferenced it unconditionally: TypeError: 'NoneType' object is not subscriptable That's what was failing this PR's "scheduler" CI job.
mikeysklar
added a commit
that referenced
this pull request
Aug 4, 2026
Second-eyes review from Hermes (verified independently against this repo's real lib/tlsf/tlsf.c, probe pack in the review thread): tlsf_create_with_pool() cannot return NULL for any region that passes MIN_HEAP_REGION_SIZE on this board. control_construct() sizes the control block from max_bytes (circuitpy_max_ram_size, a compile-time 8 MiB constant), not from the region being added -- so the control block is the same fixed size no matter which region hosts it, and both the 8 KiB guard and TLSF's actual minimum clear it by a wide margin. The `heap == NULL` branch documented a mechanism that cannot occur; removed it rather than leave dead code that reads as real handling. Also corrected the size-guard comment: measured against the real TLSF sources, a region under the guard doesn't fail to allocate from -- tlsf_create_with_pool() writes its control block past the end of the region regardless and returns success (a 1 KB region took a 3.7 KB out-of-bounds write in the measurement). The corruption from that write, not a clean "cannot allocate" failure, is what the guard actually prevents. This branch predates the board definition (added by #23) so it can't build standalone; the identical change was hardware-verified on siwx917_dk2605a via siwx917/integration, which carries this branch's exact commits plus this same fix on top: boots, gc.mem_free() reports ~8 MB free, allocation works, hw test suite 9 passed/1 xfailed (unchanged). Not touching the largest-region-first reorder: it's a correct permutation and the hazard flagged in review (relocating the heap's control block into the SiWx917's PSRAM, which sits behind an unmaintained write-back cache per #22) is already mitigated in the merged tree by disabling that cache before port_heap_init() does anything else -- and the reorder itself is unmeasured as a live bug, just a documented risk worth keeping an eye on for other boards.
zephyr_dts_to_cp_board() dereferenced props["label"] unconditionally for gpio-keys, but label is optional and deprecated there; modern boards identify keys with zephyr,code instead. Any such board failed board generation with KeyError: 'label'. Guard it the same way the gpio-leds handler a few lines above already does.
The siwx91x SoC family Kconfig never selects BUILD_OUTPUT_HEX, unlike Silicon Labs' other SoC families (silabs_s0/s1/s2/sim3, which all select it). The port's Makefile unconditionally copies zephyr.hex to firmware.hex for release artifacts, so every release build for this board failed: cp: cannot stat '.../zephyr.hex': No such file or directory make: *** [Makefile:37: build-.../firmware.hex] Error 1 even though the .elf and .rps (what's actually flashed) built and linked fine. Verified locally: zephyr.hex now generates.
Owner
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the Silicon Labs SiWx917-DK2605A (BRD2605A), plus one line in
boards/board_aliases.cmake.Four board decisions worth reviewing:
CONFIG_NET_TCP=yandCONFIG_ZVFS_OPEN_MAX=8. The board enabledNET_SOCKETS,NET_IPV4andNET_DHCPV4but never TCP, and the siwx91x driver defaults to the native stack. DHCP is UDP, so association and the lease both worked and the board looked healthy while everySOCK_STREAMsocket failed.ZVFS_OPEN_MAXalso defaulted to 0, leaving no descriptors to hand out. This is what blocked the web workflow, not anything in the workflow itself.siwx917_dk2605a.overlayexists to overrideapp.overlay, which unconditionally referenceszephyr_udc0. This SoC has no USB device controller, so there is no CIRCUITPY drive over USB, ever. Precedent:boards/nrf54l15dk_nrf54l15_cpuapp.overlay. The overlay also enables the PSRAM node, which is only safe because of #22.Console on SEGGER RTT, not the UART.
CONFIG_UART_CONSOLE=n,CONFIG_RTT_CONSOLE=y. The board has one UART and CircuitPython wants it for the REPL, so every Zephyrprintkinterleaved into the Python session and corrupted pasted input.CIRCUITPY carved out of
code_partition, not upper flash. In common flash mode the M4 cannot write flash itself, the NWP does it, and the NWP refuses writes in the OTA swap region. Everything there opens and reads fine and every write returns-EIO. 1 MB partition, 984 KB to CIRCUITPY.Depends on #15, #16, #18, #20, #21, #22.