Skip to content

zephyr-cp: rebase zephyr pin to zephyr_260803 (CI verification) - #40

Open
mikeysklar wants to merge 79 commits into
mainfrom
siwx917/integration
Open

zephyr-cp: rebase zephyr pin to zephyr_260803 (CI verification)#40
mikeysklar wants to merge 79 commits into
mainfrom
siwx917/integration

Conversation

@mikeysklar

@mikeysklar mikeysklar commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Purpose: get the rebased zephyr pin through the full board matrix. Opened against this fork's main, not upstream.

The zephyr pin moved from adafruit/zephyr@e1dc85052bc (2026-04-01, 8175 commits behind upstream) to mikeysklar/zephyr@1f5ee1ce103 — our four siwx917 commits replayed onto adafruit/zephyr:zephyr_260803 (2026-08-03, 224 behind).

Why this PR exists: build.yml only triggers on push to main/*.x or on a pull request, so a push to siwx917/integration runs pre-commit and nothing else. The board matrix has never compiled against the new pin. That matters because the zephyr pin is shared by every zephyr-cp board (nordic, nxp, renesas, st, raspberrypi), and so far only silabs_siwx917_dk2605a has been built and flashed. build.yml's ports job (the one that builds all 25 zephyr-cp boards) depends on tests passing first, so a red tests/zephyr blocks the whole board matrix from running at all.

Fixed so far, each invisible to the cherry-picks and only caught by actually building/running against the new pin:

  1. SPI_SILABS_SIWX91X_GSPI_DMA (bool) replaced by SPI_SILABS_SIWX91X_GSPI_DMA_DESCR_COUNT (int, default 3 if DMA). Assigning the old symbol is a hard Kconfig error. Fixed by deleting the assignment, matching upstream's own spi_loopback conf for this board.
  2. uart_irq_update() returns void, was int. Fixed per Zephyr's echo_bot sample. The adjacent readiness test also moved from !uart_irq_rx_ready() to <= 0, since that function returns negative on error and the old form fell through into the FIFO read instead of bailing. Pre-existing bug, surfaced by the migration.
  3. samples/bluetooth/hci_ipc/nrf5340_cpunet_iso-bt_ll_sw_split.conf was split upstream into prj.conf + extra-*.conf overlays. Fixed sysbuild.cmake to use EXTRA_CONF_FILE, and added hci_ipc_netcore.conf to restore net-core BT sizing (BT_MAX_CONN=3, ISR_STACK_SIZE=1024) that the new upstream prj.conf doesn't provide, verified against the old pin's overflow math.
  4. CONFIG_PSA_CRYPTO (a new top-level bool + provider-choice gate upstream added ahead of mbedtls's PSA provider) was never set explicitly and only worked by accident where Bluetooth transitively selected it. Fixed by adding CONFIG_PSA_CRYPTO=y + CONFIG_PSA_CRYPTO_PROVIDER_MBEDTLS=y to prj.conf. Confirmed via CI: the tests/zephyr build step, which previously failed with undefined reference to psa_hash_setup, now succeeds and progresses into running the test suite.

8175 commits of drift will plausibly have broken something on a board I can't build locally, in exactly this shape. This run is how we find out.

Currently open: tests/zephyr is red, blocking the ports board-matrix job from running. Three tests/test_web_workflow.py hostnetwork tests (native_native_sim only, CIRCUITPY_HOSTNETWORK) fail with requests.exceptions.ReadTimeout — confirmed as a real regression, not flakiness, via before/after comparison against PR #38 on the old pin. Diagnostic instrumentation (since reverted) showed accept() succeeds quickly and isn't the bottleneck; the connection is accepted but nothing further happens until the test's client-side timeout fires. Full writeup, ruled-out hypotheses, and the strongest remaining lead: #42. This only affects the native_native_sim test target (CIRCUITPY_HOSTNETWORK=1); it's invisible on real siwx917 hardware, where that config is 0.

Hardware-verified on the DK2605A before opening:

check result
gc.mem_free() 8,264,720 (was 8,265,200)
allocation churn 0 errors, 4 rounds x 60 buffers
cpu.uid 0000c09b9ec39910, unchanged
mDNS hostname cpy-c39910
flash artifact zephyr.rps, boots

The new upstream icache driver is configured in but --gc-sections drops all eight cache_instr_* functions from the ELF, so it cannot affect runtime here. The PSRAM dcache workaround at 0x44040000 is a different peripheral and is untouched.

Pre-rebase line preserved at mikeysklar/zephyr:siwx917/integration-pre-260803 if a revert is ever needed.

🤖 Generated with Claude Code

mikeysklar and others added 30 commits July 31, 2026 13:51
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.
port_heap_init() passed whichever memory region came first to
tlsf_create_with_pool(). TLSF's control structure lives in that first
pool, so a board whose first region is small ends up with a heap that
cannot hold it, and the first real allocation aborts:

    Init heap at 0x24061c00 - 0x24062000 with size 1024
    Init heap at 0 - 0x400 with size 1024
    Init heap at 0xa000000 - 0xa800000 with size 8388608
    abort()
    >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0

The existing 'size < 1024' guard does not catch it: a region of exactly
1024 bytes passes and still cannot host the control structure.

Visit the largest region first, then the rest in their original order.
On a SiWx917 dev kit this is the difference between a 7 KB heap and the
full 8 MB of PSRAM.
_event_handler() already had a NET_EVENT_WIFI_SCAN_RESULT case, but the
event was never added to the net_mgmt subscription mask, so it never
ran and every scan returned zero networks.

NET_EVENT_WIFI_RAW_SCAN_RESULT, which was subscribed, is not a
substitute: it carries raw beacon frames and only fires when
CONFIG_WIFI_MGMT_RAW_SCAN_RESULTS is enabled, which is not the default.
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.
common_hal_wifi_radio_connect() was a stub: the body was commented-out
ESP-IDF code and it returned WIFI_RADIO_ERROR_NONE without attempting
anything, so connect() silently 'succeeded' while never associating.
get_connected() returned a hardcoded false and the IPv4 getters returned
None. No zephyr-cp board could join a network.

Implement connect() with NET_REQUEST_WIFI_CONNECT:
  - build wifi_connect_req_params from ssid/password/channel/bssid
  - wait on a semaphore signalled from CONNECT_RESULT (or DISCONNECT_RESULT,
    which is how a failed attempt reports), honouring the timeout argument
    and staying interruptible
  - map wifi_conn_status to the CircuitPython error codes so a wrong
    password raises AUTH_FAIL instead of appearing to succeed
  - start DHCPv4 and wait for an address

Also implement get_connected(), get_ipv4_address() and get_ipv4_gateway()
from the Zephyr net_if state.

get_mac_address() returned an uninitialized stack buffer; read the real
address from net_if_get_link_addr() instead.

Security is currently fixed at WIFI_SECURITY_TYPE_PSK. Transition-mode
APs negotiate up from there, but a WPA3-only network needs SAE, which
cannot be inferred without consulting a prior scan.

Verified on a SiWx917-DK2605A: associates, obtains a DHCP lease, and
reports the correct MAC derived from the SoC unique ID.
…ertising

common_hal_bleio_adapter_start_advertising() called set_tx_power()
unconditionally before bt_le_adv_start(). That sends
BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL (0xfc0e), a Zephyr vendor-specific
command rather than a Bluetooth spec one. Controllers that do not
implement Zephyr's VS extensions reject it, and the resulting error
aborted start_advertising() before advertising was ever started, so
those controllers could not advertise at all.

On a SiWx917, which has its own vendor command at 0xfc06 for RF power:

    <wrn> bt_hci_core: opcode 0xfc0e status 0x01   (Unknown HCI Command)
    OSError: [Errno 5] Input/output error

Split the command into a non-raising _try_set_tx_power(). The public
common_hal_bleio_adapter_set_tx_power() still raises, so an explicit
adapter.tx_power assignment fails loudly, but advertising logs the
rejection and continues.

Verified on a SiWx917-DK2605A: the board now advertises and is
discoverable by name from an external BLE scanner.
The guard was 'size < 1024', which let a region of exactly 1024 bytes
through. On the siwx91x that admitted two regions that must never hold
Python objects: memory@0, which the SoC devicetree documents as
'reserved for the NWP (Network Processor)', and memory-dma@24061c00,
a DMA buffer.

Handing objects out of memory written by hardware outside the CPU
produces corruption that surfaces much later and far away - an int that
has become a function, a bytearray with a wrong length, or a jump
through a jumbled pointer.

Raise the bound to 8 KB. Regions smaller than that are not usefully part
of the heap anyway.
The port did not build ulab at all - build_circuitpython.py never
referenced it, so CIRCUITPY_ULAB was always 0 regardless of the usual
CIRCUITPY_FULL_BUILD default.

Add it as a per-board opt-in via CIRCUITPY_ULAB in circuitpython.toml.
Sources and flags mirror py/py.mk (MODULE_ULAB_ENABLED,
ULAB_HAS_USER_MODULE=0, -iquote on extmod/ulab/code). It is off by
default because it costs roughly 95 KB of flash.

Verified on a SiWx917-DK2605A: numpy imports and runs.
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.
Reconnecting on a live association was rejected by the driver with
-EALREADY ("Device already in active state"), and the failure path
took the interface back down, so a redundant reconnect destroyed a
working link:

    NET_EVENT_WIFI_CONNECT_RESULT status 0     <- connected
    <err> siwx91x_wifi: Device already in active state
    NET_REQUEST_WIFI_CONNECT failed: -120
    net_if_down                                <- link dropped

This is not hypothetical: CircuitPython resets the VM between the
supervisor and code.py and re-runs Wi-Fi init each time, so boot-time
auto-connect from settings.toml hit it on every boot.

Disconnect first when already associated, and treat -EALREADY from the
connect request itself as success. Matches what the commented-out ESP
implementation did.
start_web_workflow() created the listening socket, bound it and called
listen() without checking any of them, with an explicit "(Not checking for
failures)" comment on the bind. On a port whose network stack can refuse to
create a socket, the descriptor stays unset and bind() and listen() then run
on -1 and fail with EBADF, but the function still returns true. The caller
takes that as success and installs the background callback, so the supervisor
polls a socket that was never opened and the board can hang before running
code.py. Nothing is ever served, and there is no diagnostic.

Observed on a Zephyr port board, where socket() returns ENOTCONN until the
interface has an address:

    socket() ok=0 num=-1 errno=107   ENOTCONN
    bind port 80 -> 9 errno=9        EBADF
    listen -> 0 errno=9 closed=1
    (returns true)

Return false instead so the caller retries on its next invocation, and close
the socket if bind or listen is the step that failed.

This is not specific to that port. adafruit#10054 reports web workflow unreachable
after a watchdog reset on several ESP32 boards, and the attempted fix in
 adafruit#10948 moved port 80 from closed to filtered, i.e. bound but never serving,
which is the same end state this produces.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…network

connect() tore down the existing association and rebuilt it on every call,
because the radio recorded only whether it was connected and never which
network it was connected to, so the already-on-this-network case could not be
detected.

supervisor_start_web_workflow() calls connect() on every invocation, and it
runs once at startup and again on every VM reset. The result is continuous
CONNECT/DISCONNECT churn: the board re-associates forever, never holds a DHCP
lease, and the status bar sits at "Wi-Fi: No IP".

Track the SSID the current association belongs to and return
WIFI_RADIO_ERROR_NONE when connect() names it. Switching networks still
disconnects first, which is what the -EALREADY path below needs.

The comment in web_workflow.c ("it will return early if we're already
connected to the network") states this as a requirement on the port. espressif
and raspberrypi both implement it; this port did not.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
The SiWG917 has a 16 KB data cache dedicated to PSRAM (family reference
manual rev 1.2 section 5.4.5) on the QSPI2 path, at 0x44040000. The
bootloader leaves it enabled and half-configured, and nothing in a Zephyr
build can maintain it:

  - WiseConnect's own sli_si91x_psram_device_init() clears the HPROT
    allocate signal immediately after enabling the cache. That step never
    runs here: SL_SI91X_D_CACHE_ENABLE is never defined, and the macros it
    needs (DCACHE_CTRL_AND_STATUS, HPORT_ALLOCATE_SIGNAL) do not exist
    anywhere in the vendored HAL, so the path would not even compile.
  - Zephyr cannot maintain it either. cache_siwx91x.c asserts the SoC has
    no data cache, CPU_HAS_DCACHE is never selected, and sys_cache_data_*
    return -ENOTSUP.

The result is an unmaintained write-allocate cache in front of memory the
NWP also writes. Python objects corrupt under allocation churn: ints that
become function objects, bytearrays reporting wrong lengths, garbled
qstrs, unknown-bytecode fallthrough at py/vm.c, and MPU faults through
pointers read out of PSRAM.

Disable it in port_heap_init(), before any TLSF pool exists.

Two details that cost time and are worth keeping:

  - Only bit 0 of DCACHE_REG_CTRL clears. Bit 1 is sticky, and
    DCACHE_REG_MAINT_STATUS settles at 0x100 rather than 0. The vendor's
    own disable path spins on `(MAINT_STATUS & 0x3) != 0` and would hang
    on this silicon. Clear, barrier, and read back instead.
  - Setting ATTR_MPU_RAM_NOCACHE on the psram node does nothing. That is
    an MPU attribute governing the Cortex-M architectural cache, and
    CPU_HAS_DCACHE is never selected, so there is nothing for it to
    govern. This cache is a separate peripheral an MPU attribute cannot
    reach. A null result there is a null test, not a null theory.

Verified on BRD2605A: with PSRAM as the Python heap, gc.mem_free() reports
8,265,200 bytes, 1000 x bytearray(64) completes, and a 200,000 iteration
bigint accumulation returns the correct sum. All three failed on every
prior build. Independently reproduced on a second board.

This is a workaround, not a fix. The fix is a real cache driver for the
peripheral, or clearing HPROT allocate the way the vendor intended.

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>
Four printks were added by the commits on this branch and do not belong in
a merged port:

  - "wifi already connected" fires on the ordinary early-return path, which
    the supervisor hits on every invocation of supervisor_start_web_workflow().
    That is the noisiest of the four and the least informative.
  - "NET_REQUEST_WIFI_CONNECT failed" and "connect timed out" are redundant:
    both are immediately followed by a distinct wifi_radio_error_t return that
    shared-bindings turns into a Python exception, so the caller already learns
    what happened.
  - "NET_REQUEST_WIFI_DISCONNECT failed" sat in a branch whose failure is
    tolerated on purpose. Dropping the check as well as the print avoids an
    unused variable; a genuinely unusable interface still surfaces through the
    connect call below, which does return an error.

Beyond noise, these interleave with the REPL on boards where the Zephyr
console shares a UART with it, corrupting the raw REPL protocol that test
tooling depends on:

    Raw REPL did not acknowledge (got b'\x1b]')

Note the 42 remaining printks in this port's wifi path (40 in
common-hal/wifi/__init__.c, 2 in ScannedNetworks.c, 7 in Radio.c) are
pre-existing upstream code, untouched here. Removing those is a separate
question and does not belong in a feature branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mikeysklar and others added 21 commits August 3, 2026 18:23
The "~10s" figure was inferred from pytest failure behavior rather than
measured. Direct measurement (burst until refused, poll until
recovered, 3 trials) gives ~1.14s recovery after exactly 6 requests,
reproducible to two decimal places across trials and builds.

Also refutes the NET_SOCKET_MAX_SEND_WAIT hypothesis: its 10000ms
default coincidentally matched the wrong number, and changing it 5x
moved the window by under a millisecond. No config change kept.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RW6sWt4Kb6WpafgHggmTt
Two failures, both ours, both caught by CI on every open PR.

check-translate: the GATT server work added four new MP_ERROR_TEXT
strings, which ports/zephyr-cp/AGENTS.md explicitly prohibits ("Do not
add new translatable error strings... use raise_zephyr_error() or
CHECK_ZEPHYR_RESULT()"). Converted to raise_zephyr_error() with the
errno that matches each condition: -ENOSPC for the two fixed-table
exhaustion checks, -EALREADY for re-registering a service, -EINVAL for
a negative max_length. Regenerating locale/circuitpython.pot would have
silenced the check while leaving the rule broken.

Deliberately NOT converted: "Value length != required fixed length" and
"Value length > max_length". Those were never part of the failure -
they are already in the .pot because nordic and espressif raise the
same messages for the same conditions - and an errno would lose
information the user needs while diverging from the other ports.

ruff C901: build_circuitpython() reached complexity 41 (limit 40) via
the ulab opt-in commit df644fe. Extracted configure_ulab_module();
ruff passes clean on main, so this was our regression. Suppressing with
noqa or raising the threshold would have hidden a real one-over.

Verified: check-translate ok, ruff all checks passed, firmware builds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RW6sWt4Kb6WpafgHggmTt
Fixes the "tests / zephyr" CI failure, which was ours and which local
hardware testing could never have caught:

  Service.c:104: undefined reference to `bt_gatt_service_unregister'
  Service.c:138: undefined reference to `bt_gatt_service_register'
  (build-native_nrf5340bsim)

The GATT server added in 4f783df lives in the shared
common-hal/_bleio/, so it compiles for every zephyr-cp board, but only
siwx917_dk2605a got CONFIG_BT_GATT_DYNAMIC_DB. Both functions are
inside `#if defined(CONFIG_BT_GATT_DYNAMIC_DB)` in
zephyr/subsys/bluetooth/host/gatt.c (lines 1527-1728), so the other
eight BT-enabled boards failed to link. Added to all of them.

Not verified locally: nrf5340bsim needs babblesim, which is not in this
checkout ("No board named nrf5340bsim_nrf5340_cpuapp found"), so CI is
the check. What is verified is that the symbol is the correct one -
the Kconfig guard around both functions is quoted above - and that
siwx917 still builds.

Also applies ruff format to test_web_workflow_hw.py; my line wrapping
was tighter than ruff's and `ruff format` is a separate pre-commit hook
from `ruff check`, which is why the first fix pass missed it. Full
`pre-commit run --all-files` now passes all nine hooks locally.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RW6sWt4Kb6WpafgHggmTt
…tring

The extraction dropped "Flags mirror py/py.mk", which is the one line
telling a future reader where to look when these flags need updating.
Restored into the docstring.

Verified the extraction is behavior-preserving on the path that matters:
siwx917_dk2605a is the only board with CIRCUITPY_ULAB = true, and its
build produces 32 ulab objects and 36 ulab symbols in the ELF, so the
enabled branch still compiles and links ulab.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RW6sWt4Kb6WpafgHggmTt
…ed there)

My sweep in b5eb970 tested `"CONFIG_BT=y" not in s` as a substring,
which matched the COMMENTED line `# CONFIG_BT=y` in this board's conf.
BT is off for stm32wba65i_dk1, so the symbol has unmet dependencies
(BT_GATT_DYNAMIC_DB depends on BT_GATT_SERVICE_CHANGED) and is dead
config on a board that cannot use it.

Correct scope: 9 boards enable BT, siwx917 already had the symbol, so
8 needed it. Re-verified with an anchored match, and confirmed no board
now carries the symbol without BT enabled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RW6sWt4Kb6WpafgHggmTt
The commit adding CONFIG_BT_GATT_DYNAMIC_DB to the BT boards noted the
fix was unverified locally because "No board named
nrf5340bsim_nrf5340_cpuapp found". That was my error, not a missing
board: the build target is the alias in boards/board_aliases.cmake, not
the .conf filename.

  cp_board_alias(nordic_nrf5340dk nrf5340dk/nrf5340/cpuapp)

Building the alias works, and nrf5340dk is one of the eight boards that
needed the symbol:

  make BOARD=nordic_nrf5340dk   -> exit 0
  [438/438] Linking C executable zephyr/zephyr.elf
  undefined reference to bt_gatt_service*  -> 0 occurrences

So the fix is confirmed by a real link on a non-siwx917 board, not
inferred from the Kconfig guard alone. Credit to Hermes for spotting
that the conf filename and the west build argument are different
strings.

Still not built here: the bsim variant, which dies inside babblesim on
macOS with "ls: --hide=common: No such file or directory" (GNU vs BSD
ls) before reaching the linker. nrf5340dk covers the same link path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RW6sWt4Kb6WpafgHggmTt
NET_MAX_CONN is the constraining pool for incoming connections. At 8,
the workflow refused every connection after a burst of ~6 zero-spacing
requests and recovered ~1.14s later. Raising it to 12 eliminates it.

Measured A/B/A on hardware, burst-until-refused, 3 trials per arm:

  NET_MAX_CONN=8   refused after 6, recovery 1.11-1.14s
  NET_MAX_CONN=12  200 consecutive requests, zero refusals (x2)
  NET_MAX_CONN=8   refused after 6 again - symptom returns

The revert arm is the part that makes this causal rather than
coincidental. It is a race, not a hard ceiling: at 8 slots a zero-spacing
burst consumes connections faster than teardown recycles them, which is
also why spacing requests >=100ms avoided it.

Ruled out by measurement, each varied in isolation with no effect on the
boundary: NET_SOCKET_MAX_SEND_WAIT (10000->2000), ZVFS_OPEN_MAX (8->16),
NET_MAX_CONTEXTS (10->14). That last null result is what rules out the
TCP connection slab, which NET_MAX_CONTEXTS sizes - and it refutes the
mechanism both benches had converged on.

Two test-harness bugs surfaced while confirming the fix, both mine:

- The shared requests.Session (added earlier to work around this very
  pool exhaustion) became the liability once the pool was fixed: the
  board closes idle keep-alives, so pooled reuse died with ECONNRESET
  (errno 54) - a different error than the ECONNREFUSED (errno 61) this
  issue is about. Dropped the session; a fresh connection per request is
  what the fix makes affordable and what 200 clean requests validated.
- test_http_latency_sane ran right after the PUT/DELETE test, whose
  filesystem writes trigger auto-reload, so it was timing a board restart
  rather than the listener. Added wait_until_stable().

The three xfail pins for this issue are removed; the suite now reports
6 passed, 1 xfailed on hardware, the remaining pin being #34.

Fixes #35.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RW6sWt4Kb6WpafgHggmTt
Regenerated by building that board to verify the BT_GATT_DYNAMIC_DB fix.
The staleness pre-dates this work: the added entries (audiofilewriter,
audioi2sin, usb_audio) are upstream modules that appeared after the file
was last generated, not a consequence of any change here. The build
warns when this file is out of date and asks for it to be committed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RW6sWt4Kb6WpafgHggmTt
…slab

The fix commit established NET_MAX_CONN empirically via A/B/A but could
not say why raising the TCP connection slab (NET_MAX_CONTEXTS) did
nothing. Hermes found the missing path in Zephyr's source: a TCP
connection consumes two separate resources.

  tcp.c          tcp_conns_slab      sized by NET_MAX_CONTEXTS
  connection.c:60 conns[]            sized by NET_MAX_CONN
  tcp.c:2566     every TCP connection also registers a packet handler
                 into conns[]

conns[] is the binding constraint, so the slab null result was real and
NET_MAX_CONN was decisive. Their original 8-2=6 arithmetic was correct;
the "correction" that withdrew it was the error.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RW6sWt4Kb6WpafgHggmTt


wifi_radio_get_ipv4_address() - the raw uint32_t getter that
supervisor/shared/web_workflow/web_workflow.c uses internally for the
status bar and /cp/version.json's "ip" field - was a leftover ESP-IDF
stub, unconditionally returning 0. This is a distinct function from
common_hal_wifi_radio_get_ipv4_address() (the Python-facing mp_obj_t
getter, already fixed in feat-wifi-per-ap-security): same underlying
address, two separate C entry points, only one had been fixed.

Verified on hardware: GET /cp/version.json now returns
"ip": "192.168.0.133", matching the board's real DHCP lease.

board_name and hostname remain empty. Root cause is unrelated to the ip
bug: both come from the mDNS responder (CIRCUITPY_MDNS), and this port
has no common-hal/mdns implementation at all - confirmed via
compile_commands.json (no CIRCUITPY_MDNS define reaches
web_workflow.c) and the built ELF (zero mdns symbols). Only espressif
and raspberrypi implement it. That is a new common-hal component, not a
bug fix, so it is split off as #37 rather than folded into this commit
or left silently unfixed under #34's title.

test_version_json_runtime_fields_populated is split into
test_version_json_ip_populated (real assertion now, xfail removed) and
test_version_json_mdns_fields_populated (xfail against #37).

Fixes #34.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RW6sWt4Kb6WpafgHggmTt
Found by Hermes: "two entry points for one value, only one fixed" is
rarely singular, so they swept their tree for the same shape and it
turned up common_hal_wifi_radio_get_addresses() - a real STA-mode
Python property, distinct from the get_addresses_ap() stub next to it,
which was AP mode and genuinely unimplemented.

common_hal_wifi_radio_get_addresses() returned mp_const_none
unconditionally. Wrong type for the shared-bindings contract
("addresses: Sequence[str] ... Empty sequence when not connected") in
both states: None instead of a tuple when connected, None instead of
() when not.

Reuses wifi_radio_get_ipv4_address() (fixed for #34) and formats it as
a string, matching the Sequence[str] contract other ports follow -
espressif and raspberrypi both return string tuples here, not
IPv4Address objects, which is why get_addresses formats rather than
calling common_hal_ipaddress_new_ipv4address like the ipv4_address
property does.

get_addresses_ap() also returned mp_const_none; corrected to
mp_const_empty_tuple for the same type contract without claiming AP
mode works.

Verified on hardware: wifi.radio.addresses == ('192.168.0.133',),
addresses_ap == (). New test_wifi_radio_addresses covers it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RW6sWt4Kb6WpafgHggmTt
Two unrelated fixes found while building the mDNS common-hal (#37):

wifi_radio_get_mac_address(self, uint8_t*) was declared in
shared-bindings/wifi/Radio.h but never implemented for this port -- only
the Python-facing common_hal_wifi_radio_get_mac_address() existed. The
mDNS server needs the raw bytes for its default "cpy-XXXXXX" hostname,
same as every other port, so add the raw helper and have the existing
function call it instead of duplicating the netif read.

common_hal_wifi_radio_get_ipv4_gateway/_subnet read net_if_ip.ipv4
unconditionally, but that struct member only exists when CONFIG_NET_IPV4
is set (zephyr/include/zephyr/net/net_if.h). This file is compiled for
every Wi-Fi board in the port's CI matrix, not just siwx917, and
nordic_nrf7002dk doesn't turn IPv4 on -- breaking its cross-board build
(PR #24). Guard both behind #if defined(CONFIG_NET_IPV4).
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.
/cp/version.json's board_name/hostname fields come from mdns.Server, and
this port never had a common-hal/mdns implementation, so
CIRCUITPY_MDNS was never defined and web_workflow.c's mDNS block
compiled out entirely (split off from #34).

Built on Zephyr's own mDNS responder (CONFIG_MDNS_RESPONDER) and DNS-SD
(CONFIG_DNS_SD, CONFIG_MDNS_RESPONDER_DNS_SD):

- hostname get/set delegate straight to net_hostname_get()/_set(), the
  same underlying store wifi.radio.hostname already uses on this port,
  so both stay in sync. Server construct sets the "cpy-XXXXXX" default
  from the MAC, matching every other port's contract.
- advertise_service() builds a struct dns_sd_rec per service (up to
  MDNS_MAX_SERVICES) and hands them to
  mdns_responder_set_ext_records() -- TXT records length-prefixed per
  RFC 6763 into a per-service buffer.
- find() is an honest empty result, not a fake search: Zephyr's mDNS
  stack (subsys/net/lib/dns) is responder-only, it has no client that
  sends PTR/SRV queries and parses replies. dns_resolve_name(...,
  DNS_QUERY_TYPE_PTR, ...) resolves one already-known name; it isn't a
  browse. Documented in Server.c and boards/siwx917_dk2605a.conf.

Module enablement follows the same pattern as _bleio's CONFIG_BT scan
in cptools/zephyr2cp.py: board_info["mdns"] is set from whether
CONFIG_MDNS_RESPONDER=y is actually in the board's .config, so boards
that don't opt in via Kconfig don't get CIRCUITPY_MDNS=1 and won't hit
this common-hal at all.

Hardware-verified on siwx917_dk2605a: wifi.radio.hostname reads back
"cpy-c39910", /cp/version.json's board_name and hostname are both
populated, hardware suite is 9 passed (up from 8, the mdns-fields test
is no longer xfail). Also added test_mdns_hostname_resolves_over_network
for real on-air resolution, marked xfail: this network's AP appears to
block multicast between wireless clients (a control browse for
_services._dns-sd._udp turned up nothing from any other device either),
so it couldn't be confirmed end-to-end here even though the responder
side is verified correctly configured.
Second-eyes review from Hermes on #16 (verified independently against
this repo's real lib/tlsf/tlsf.c, probe pack attached in Slack):

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.

Hardware-verified on siwx917_dk2605a: boots, gc.mem_free() reports
~8 MB free, allocation works, hw test suite 9 passed/1 xfailed
(unchanged).
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.
Zephyr has no hwinfo driver for siwx91x, so hwinfo_get_device_id() returns
-ENOSYS and cpu.uid (and the web workflow's version.json UID field) read as
all zeros.

A hwinfo driver against the efuse controller would not help: the efuse
identity region (0x020..0x02F of the array behind TA_EFUSE_IO_BASE_ADDR) is
unprogrammed on this silicon - verified on two independent dies (this one and
Hermes's DK2605A), each carrying only 4 nonzero calibration bytes at
0x3A..0x3D and zeros where efusecopy holds the MACs. Simplicity Commander's
own 'Unique ID' for this part is the WiFi MAC zero-extended to 8 bytes, read
from the flash-resident config space, not the efuse macro.

Match that definition: when hwinfo yields nothing, build the UID from the
wifi net_if link address as 00 00 | mac[6]. The link address is populated at
driver init before the radio is enabled, so it is available whenever this
can be called. Guarded on CONFIG_SOC_FAMILY_SILABS_SIWX91X so no other
board's uid semantics change.

Hardware-verified: version.json now reports UID 0000C09B9EC39910, matching
commander device info byte for byte. Diagnosis of the empty efuse array by
Phil Torrone / Hermes; on-die verification on both benches.

Also drops the two debug printk lines on the failure path (issue #8).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flagged by Hermes review (#the-forge, 2026-08-04): the MAC-fallback value is
a WiFi MAC that Silicon Labs' own mfg917 tool can reprogram, not a burned-in
constant. Nothing on this silicon offers an immutable alternative -- no die
ID in the RM, no unique_id concept in the vendor SDK, and the one real
per-device secret (a PUF-derived key) is deliberately non-readable as
plaintext -- so the fix stands, but callers relying on cpu.uid as a
tamper-proof hardware serial (license binding, provisioning) need to know
this port doesn't provide one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ndency

Flagged by Hermes review (#the-forge, 2026-08-04): the largest-region reorder
puts TLSF's control block in PSRAM on the SiWx917, which is only safe
because the dcache disable a few lines above already ran with no allocation
between them. Nothing in the function enforces that order. A comment costs
nothing and stops a future edit from silently reintroducing the
write-allocate corruption the disable exists to prevent.

Comment-only change; ROM checksum and image CRC unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Moves the zephyr pin from adafruit/zephyr circuitpython10.3.x (2026-04-01,
8175 commits behind upstream) to our fork's siwx917/rebase-260803, which
replays our four siwx917 commits onto adafruit/zephyr zephyr_260803
(2026-08-03, 224 behind upstream).

Rebase target note: 7 of the 12 commits our fork carried ahead of upstream
are Adafruit's own CircuitPython patches (perfetto tracing, gpio/I2S/flash
emulation, headless displays), not ours. Rebasing onto bare
zephyrproject-rtos/zephyr would have silently dropped them; zephyr_260803 is
the same Adafruit series already replayed onto current upstream.

Two API changes in the gap, neither visible to the cherry-picks -- all four
commits applied clean and only the build caught these:

1. SPI_SILABS_SIWX91X_GSPI_DMA (bool) was replaced by
   SPI_SILABS_SIWX91X_GSPI_DMA_DESCR_COUNT (int, "default 3 if DMA", 0
   disables). Assigning the old symbol is now a hard Kconfig error. DMA
   stays on via the existing CONFIG_DMA=y, matching upstream's own
   spi_loopback conf for this board.

2. uart_irq_update() returns void, was int. Call it unconditionally and
   query readiness separately, per Zephyr's echo_bot sample. The adjacent
   readiness test moves from !uart_irq_rx_ready() to <= 0: that function
   returns negative on error, so the old form fell through into the FIFO
   read loop on error instead of bailing. Pre-existing bug, surfaced here.

Hardware-verified on the DK2605A:

  gc.mem_free()          8,264,720   (was 8,265,200 pre-rebase)
  allocation churn test  0 errors    (4 rounds x 60 buffers, retained-buffer
                                      integrity checked across collections)
  cpu.uid                0000c09b9ec39910   unchanged, matches commander
  mDNS hostname          cpy-c39910         still advertising
  flash artifact         zephyr.rps         runner fix works on the new base

The new upstream icache driver (13a21bbe1) is configured in
(CONFIG_CACHE_SILABS_SIWX91X=y) and compiles, but --gc-sections drops all
eight cache_instr_* functions from the final ELF because nothing calls the
cache API: zero cache symbols in zephyr.elf against eight in the object
file. It cannot affect runtime behavior here, which matches its own commit
message's "no functional impact" claim. Our PSRAM dcache workaround at
0x44040000 is a different peripheral (M4SS bus-side, not the CPU cache) and
is untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Found by building nordic_nrf7002dk locally against the new pin; CI never
compiled it (see below).

Upstream split samples/bluetooth/hci_ipc/nrf5340_cpunet_iso-bt_ll_sw_split.conf
into a base prj.conf plus extra-*.conf overlays, so sysbuild.cmake's reference
to the old filename is a hard CMake error on every nRF5340-based board
(nrf7002dk, nrf5340dk, nrf5340bsim).

Two changes, both verified against upstream's own net-core sysbuild files
(tests/bsim/bluetooth/ll/bis/sysbuild.cmake):

- point at extra-iso-bt_ll_sw_split.conf
- use EXTRA_CONF_FILE, not CONF_FILE. CONF_FILE replaces prj.conf, which
  would silently drop the base settings (IPC_SERVICE, MBOX, BT_HCI_RAW,
  BT_MAX_CONN) the old combined file carried. That would likely still link
  and then misbehave on hardware.

hci_ipc_netcore.conf restores the two values the pre-rebase build used that
neither prj.conf nor the upstream overlay provides: BT_MAX_CONN=3 (prj.conf
uses 16) and ISR_STACK_SIZE=1024 (the overlay raises it to 1536). Confirmed
against the resolved .config rather than assumed.

KNOWN REMAINING ISSUE: the nRF5340 net-core image still overflows RAM by
820 bytes. That is not a migration error. Every setting differing from the
pre-rebase build is now restored, and the ISR_STACK_SIZE fix moved the
overflow 1332 -> 820, exactly the 512-byte delta it should. The residual is
growth in the new zephyr itself, so closing it means changing behavior
(trimming buffers, or dropping ISO which _bleio does not use) rather than
restoring it. Tracked separately; nordic boards are unbuildable until then.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mikeysklar and others added 3 commits August 4, 2026 18:48
…channel

Fixes tests/zephyr on the rebased zephyr pin. This was blocking the entire
zephyr-cp board matrix in CI: the 'ports' job needs [scheduler, mpy-cross,
tests], and tests/zephyr failing caused ports to be skipped by GitHub's
default needs-gating -- unrelated to what the scheduler computed (verified
separately: ci_set_matrix.py correctly resolves all 25 zephyr-cp boards for
this PR's changed files, reproduced locally).

Symptom, native_native_sim only:

  undefined reference to psa_hash_setup / psa_hash_update / psa_hash_clone
  / psa_hash_finish (shared-module/hashlib/__init__.c, Hash.c)

Confirmed a rebase regression, not pre-existing: PR #38 (old pin) has
tests/zephyr passing; same content otherwise, new pin fails.

Root cause: native_sim's resolved .config has '# CONFIG_PSA_CRYPTO is not
set', despite this file already setting CONFIG_MBEDTLS_PSA_CRYPTO_C=y and
CONFIG_PSA_WANT_ALG_SHA_256=y. Upstream restructured mbedtls into a
PSA_CRYPTO bool + PSA_CRYPTO_PROVIDER choice gate sitting in front of the
provider select (zephyr/modules/mbedtls/Kconfig.psa.logic) -- neither of the
existing lines sets that top-level gate directly. It only worked by side
channel: boards with Bluetooth enabled transitively select PSA_CRYPTO
through BT's own crypto use. native_sim doesn't build BT, so nothing pulled
it in, and hashlib -- which needs PSA hash unconditionally -- lost its
backend.

Set the gate explicitly so hashlib does not depend on BT (or anything else)
happening to select it.

Verified on hardware: rebuilt silabs_siwx917_dk2605a with this change,
ROM checksum is byte-identical to the last known-good build
(0x51291d72) -- confirms PSA_CRYPTO was already effectively on there via
the BT side channel, so this is a true no-op on boards where it already
worked and additive only where it did not.

Could not verify locally on native_native_sim itself: its POSIX
architecture is Linux-only, this dev machine is macOS. Verification is via
this branch's next CI run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tall

Investigating: three test_web_workflow.py hostnetwork tests time out
(ReadTimeout, 1.0s client timeout) on native_native_sim after the zephyr
rebase; two simpler status-line tests in the same file still pass. Confirmed
a real regression via before/after CI comparison (all five passed reliably
on the pre-rebase pin).

Candidate mechanism: this file's CIRCUITPY_HOSTNETWORK accept loop sets
SO_RCVTIMEO=1ms on the listening socket via zsock_setsockopt() with no
return-value check, then calls zsock_accept(). That option maps to the NSOS
driver's recv_timeout field (drivers/net/nsos_sockets.c), whose default for
newly-created/accepted sockets changed from undefined to K_FOREVER in
upstream commit 109826 ('fix uninitialized accepted socket', merged
2026-05-29, already in our pin). If the setsockopt call fails to propagate
on this pin, accept() would block on the new K_FOREVER default instead of
returning after ~1ms, hanging the single-threaded VM until a connection
arrives -- which would explain the symptom (connect() succeeds, since the
host TCP stack ACKs independently of the app calling accept(); response
never arrives because accept() itself never returns to let the response
path run).

Logs only on: setsockopt failure, a successful accept, or an accept() call
that took over 5ms -- avoids flooding the log with the expected common case
(EAGAIN in ~1ms).

Cannot verify locally: CIRCUITPY_HOSTNETWORK=0 for every board this Mac can
build; the only target that compiles this branch is native_native_sim,
which requires Linux (POSIX arch). This commit is diagnostic-only and
should be reverted once the mechanism is confirmed or ruled out.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Removes the TEMP printk instrumentation added in 2faeaee for
investigating the test_web_workflow.py hostnetwork ReadTimeout
regression on native_native_sim. The diagnostic data has been
collected and analyzed (see the new issue filed for the regression
itself); this restores socketpool_socket_accept() to its pre-
diagnostic form. No functional change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
mikeysklar and others added 2 commits August 5, 2026 08:17
The nRF5340 net-core image overflowed RAM by 820 bytes on the rebased
zephyr pin. Every pre-rebase setting had already been restored; the
residual was upstream growth, so closing it required cutting something.

Design by Hermes (#the-forge, 2026-08-05), measured here: replace the
EXTRA_CONF_FILE pair (upstream's extra-iso-bt_ll_sw_split.conf + our
override file) with a single file carrying the overlay's controller
baseline minus ISO and extended advertising, plus the pre-rebase sizing
(ISR_STACK_SIZE=1024, BT_MAX_CONN=3). Layering 'overlay enables ISO, our
file disables it' in one merge list can never work: clearing the four
BT_ISO_* leaves makes BT_ISO's whole 'if' block vanish, and the overlay's
ISO buffer ints become assignments to nonexistent symbols -- the
orphaned-int Kconfig failure observed on the first attempt.

_bleio uses legacy advertising and scanning only: zero bt_iso_*,
bt_le_ext_adv_* or bt_le_per_adv_* call sites anywhere in the port
(verified by grep on both trees). The port's socs/nrf5340_cpunet.conf
always intended to disable ISO but has never applied -- Zephyr resolves
socs/ fragments from APPLICATION_CONFIG_DIR, which for the hci_ipc image
is upstream's sample dir, not our port dir (also Hermes's finding).

Measured, cpunet RAM (64 KB region):

  before   overflowed by 820 B          (unbuildable)
  nrf7002dk    39,512 B   60.3% used    links clean
  nrf5340dk    37,952 B   57.9% used    links clean

Roughly 25 KB reclaimed; Hermes's prediction that the extended-advertising
allocations (SCAN_DATA_LEN_MAX=1650, ADV_DATA_LEN_MAX=191, three _SET=3
tables) were the bulk of it is confirmed by measurement. Resolved .config
verified: no BT_ISO, no BT_EXT_ADV, ISR_STACK_SIZE and BT_MAX_CONN both
took. App cores link comfortably on both boards.

Not verified: native_nrf5340bsim, the third affected target -- it needs
the bsim host toolchain this machine doesn't have. CI covers it once the
board matrix runs (blocked on #42).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Building nordic_nrf7002dk on the rebased zephyr regenerates the board info
with three module entries new to this pin (audiofilewriter, audioi2sin,
usb_audio, all disabled). Mechanical output of the build tooling.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant