aen-cc3501e-gatt-register
Bench-validation gate for the CC3501E dynamic GATT registration path on the Alif M55-HE core (E1M-AEN801, Alif Ensemble E8). alp_ble_gatt_register_service() (<alp/ble.h>) used to be a stub; it now really registers a service on the CC3501E's NimBLE host over the inter-chip bridge and fills in a real attribute handle per characteristic.
This is a strict PASS/FAIL gate, not a teaching example — the first step that fails stops the run. For the full-surface, guarded/non-fatal companion demo see aen-cc3501e-companion-tour.
Source: examples/aen/aen-cc3501e-gatt-register/.
Peer-free by design
There is no BLE central on this bench, so full central-side service discovery (a real central connecting and reading the characteristics by their discovered handles) is HIL-deferred. What it proves without a peer:
- registration returns real (non-zero) attribute handles;
- the bridge is still alive after registration — the on-silicon check for a NimBLE double-
ble_gatts_start()use-after-free the register path used to be able to trigger; - the register-before-advertise ordering guard: NimBLE's
ble_gatts_mutable()refuses to rebuild the GATT table once the stack is advertising, so registering again while advertising must come backALP_ERR_BUSY.
The six-step PASS contract
1. cc3501e_bridge_bringup() + PING -> ALP_OK (silicon-dependent)
2. alp_ble_open() -> non-NULL (silicon-dependent)
3. alp_ble_gatt_register_service() (2 chars) -> ALP_OK + both (silicon-dependent)
handles non-zero
4. a second bridge PING, post-register -> still ALP_OK (silicon-dependent; the UAF-fix check)
5. alp_ble_advertise_start() (<=8-char name) -> ALP_OK (silicon-dependent)
6. register AGAIN while advertising -> ALP_ERR_BUSY (silicon-dependent)
Every step needs the real CC3501E firmware answering over the bridge, so every step is silicon-dependent — there is no host-deterministic sub-check. Steps 1/4 are liveness probes (PING); steps 2/3/5/6 exercise the BLE surface under test.
Step 6 is expected to take several seconds to return: the driver retries across its full poll budget before the persistent ble_gatts_mutable() guard failure is remapped to ALP_ERR_BUSY (a fast-path EBUSY signal is a known follow-up).
A single RESULT PASS: ... line means every step held; the first failing step prints RESULT FAIL: <which step> and the app stops there — read from ram_console_buf over SWD (this rig has no UART on USB).
Firmware requirement
This app exercises the dynamic GATT-registration firmware handler on the CC3501E, so it needs the matching CC3501E firmware image on the on-module coprocessor. An older firmware answers with the old stub behaviour, not the fix under test — see the SDK example README for the exact firmware branch and build_ti.sh build.
SoM bring-up is one call
Same reusable cc3501e_bridge_bringup() SoM template as aen-cc3501e-companion-tour — the inter-chip wiring is unchanged from that example; see its README for the wiring table and the two-J-Link bench recipe.
Status
- native_sim/native/64 (
twister,build_only) — against the emulated SPI controller +alp,pin-arrayin the shipped overlay. Proves the build only; the emulated CC3501E backend has no real firmware behind it, so the PASS contract runs meaningfully only on real AEN silicon. alp_e1m_aen801_m55_he/ae822fa0e5597ls0/rtss_he— the real bench target.
See also
aen-cc3501e-ble-gatt— the portable GATT-server advertise pathaen-cc3501e-companion-tour— the full-surface Wi-Fi + BLE tour<alp/ble.h>reference- Examples overview