tan-cli 0.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. tan/__init__.py +0 -0
  2. tan/__main__.py +120 -0
  3. tan/cli.py +939 -0
  4. tan/command_table.py +265 -0
  5. tan/commands/__init__.py +8 -0
  6. tan/commands/bootstrap_cmd.py +4856 -0
  7. tan/commands/bootstrap_patches.py +164 -0
  8. tan/commands/bootstrap_toolchain_fetch.py +155 -0
  9. tan/commands/bootstrap_toolchain_pin.py +36 -0
  10. tan/commands/build/__init__.py +1 -0
  11. tan/commands/build/configure_inputs.py +277 -0
  12. tan/commands/build/execute.py +1852 -0
  13. tan/commands/build/host_python.py +59 -0
  14. tan/commands/build/link_stale.py +78 -0
  15. tan/commands/build/manifest.py +428 -0
  16. tan/commands/build/materialise.py +81 -0
  17. tan/commands/build/token_substitution.py +381 -0
  18. tan/commands/build/toolchain.py +482 -0
  19. tan/commands/build/user_defines_wipe.py +115 -0
  20. tan/commands/build/workspace_patches.py +65 -0
  21. tan/commands/build_cmd.py +2236 -0
  22. tan/commands/build_output.py +331 -0
  23. tan/commands/clean_cmd.py +1183 -0
  24. tan/commands/completion_cmd.py +786 -0
  25. tan/commands/debug_config_cmd.py +2425 -0
  26. tan/commands/diff_cmd.py +940 -0
  27. tan/commands/doctor_cmd.py +4767 -0
  28. tan/commands/examples_cmd.py +644 -0
  29. tan/commands/explain_cmd.py +1319 -0
  30. tan/commands/faultdecode_cmd.py +931 -0
  31. tan/commands/flash_atoc_guard.py +240 -0
  32. tan/commands/flash_cmd.py +5731 -0
  33. tan/commands/flash_mram_guard.py +150 -0
  34. tan/commands/flash_ram.py +578 -0
  35. tan/commands/flash_raw.py +583 -0
  36. tan/commands/generate_cmd.py +1456 -0
  37. tan/commands/image_cmd.py +753 -0
  38. tan/commands/init_cmd.py +1740 -0
  39. tan/commands/inspect_cmd.py +377 -0
  40. tan/commands/kconfig_cmd.py +734 -0
  41. tan/commands/model_cmd.py +1514 -0
  42. tan/commands/model_device_cmd.py +351 -0
  43. tan/commands/model_device_live.py +234 -0
  44. tan/commands/model_host_cmd.py +430 -0
  45. tan/commands/model_zoo_cmd.py +363 -0
  46. tan/commands/monitor_actions.py +125 -0
  47. tan/commands/monitor_cmd.py +626 -0
  48. tan/commands/monitor_session.py +505 -0
  49. tan/commands/new_som_cmd.py +1522 -0
  50. tan/commands/pinmux_cmd.py +568 -0
  51. tan/commands/presets_cmd.py +1123 -0
  52. tan/commands/probe_cmd.py +421 -0
  53. tan/commands/reset_cmd.py +354 -0
  54. tan/commands/reset_confirm.py +168 -0
  55. tan/commands/run_cmd.py +637 -0
  56. tan/commands/scaffold_cmd.py +490 -0
  57. tan/commands/sdk_cmd.py +1723 -0
  58. tan/commands/size_cmd.py +1020 -0
  59. tan/commands/support_bundle_cmd.py +1073 -0
  60. tan/commands/trace_cmd.py +341 -0
  61. tan/commands/validate_cmd.py +2009 -0
  62. tan/commands/west_forward_cmd.py +736 -0
  63. tan/commands/workspace_patch_check.py +296 -0
  64. tan/core/__init__.py +1 -0
  65. tan/core/alp_conf_pregen.py +100 -0
  66. tan/core/artifact_provenance.py +240 -0
  67. tan/core/atoc_guard.py +276 -0
  68. tan/core/atoc_guard_messages.py +232 -0
  69. tan/core/atoc_replacement.py +118 -0
  70. tan/core/atomic_write.py +178 -0
  71. tan/core/board_context.py +351 -0
  72. tan/core/board_diagnostic.py +111 -0
  73. tan/core/board_files.py +221 -0
  74. tan/core/board_validator.py +542 -0
  75. tan/core/board_validator_compat.py +196 -0
  76. tan/core/board_validator_run.py +129 -0
  77. tan/core/board_validator_schema.py +186 -0
  78. tan/core/board_validator_skew.py +46 -0
  79. tan/core/board_yaml_edit.py +295 -0
  80. tan/core/board_yaml_pos.py +118 -0
  81. tan/core/bootstrap.py +2572 -0
  82. tan/core/build_plan.py +625 -0
  83. tan/core/consent.py +104 -0
  84. tan/core/console_filter.py +245 -0
  85. tan/core/debug_launch.py +1803 -0
  86. tan/core/devicetree_lint.py +168 -0
  87. tan/core/dir_removal.py +229 -0
  88. tan/core/doctor_git.py +172 -0
  89. tan/core/doctor_libraries.py +253 -0
  90. tan/core/doctor_render.py +185 -0
  91. tan/core/doctor_scope.py +66 -0
  92. tan/core/doctor_setools.py +237 -0
  93. tan/core/doctor_stale.py +180 -0
  94. tan/core/document_guards.py +723 -0
  95. tan/core/dp_id.py +97 -0
  96. tan/core/elf_load.py +63 -0
  97. tan/core/error_catalog.py +171 -0
  98. tan/core/example_catalog.py +499 -0
  99. tan/core/example_facets.py +151 -0
  100. tan/core/faultdecode.py +521 -0
  101. tan/core/flash_plan.py +2501 -0
  102. tan/core/flow_d_report.py +381 -0
  103. tan/core/flow_d_uptime.py +164 -0
  104. tan/core/fs_confine.py +130 -0
  105. tan/core/global_flags.py +230 -0
  106. tan/core/host_python.py +210 -0
  107. tan/core/image_bundle.py +247 -0
  108. tan/core/inert.py +147 -0
  109. tan/core/jlink_binary.py +128 -0
  110. tan/core/jlink_probe.py +481 -0
  111. tan/core/jsonc_splice.py +404 -0
  112. tan/core/launch_provenance.py +257 -0
  113. tan/core/link_refusal.py +76 -0
  114. tan/core/metadata_schema.py +376 -0
  115. tan/core/model_check.py +227 -0
  116. tan/core/model_device.py +449 -0
  117. tan/core/model_doctor.py +198 -0
  118. tan/core/model_host.py +48 -0
  119. tan/core/model_list.py +112 -0
  120. tan/core/model_zoo.py +353 -0
  121. tan/core/module_template.py +340 -0
  122. tan/core/mram_link.py +87 -0
  123. tan/core/new_som_ospi.py +94 -0
  124. tan/core/os_class.py +159 -0
  125. tan/core/patch_paths.py +87 -0
  126. tan/core/pending.py +40 -0
  127. tan/core/plain_zephyr_plan.py +138 -0
  128. tan/core/plan_exec.py +373 -0
  129. tan/core/plan_tokens.py +600 -0
  130. tan/core/probe.py +155 -0
  131. tan/core/probe_plan.py +237 -0
  132. tan/core/proxy.py +168 -0
  133. tan/core/publish.py +31 -0
  134. tan/core/python_floor.py +178 -0
  135. tan/core/ram_run.py +694 -0
  136. tan/core/ram_watch.py +231 -0
  137. tan/core/raw_write.py +160 -0
  138. tan/core/reset_plan.py +56 -0
  139. tan/core/run.py +161 -0
  140. tan/core/scaffold.py +2203 -0
  141. tan/core/scaffold_selftest_identity.py +225 -0
  142. tan/core/sdk_default_registry.py +565 -0
  143. tan/core/sdk_discovery.py +996 -0
  144. tan/core/sdk_removal.py +465 -0
  145. tan/core/serial_actions.py +175 -0
  146. tan/core/serial_capture.py +185 -0
  147. tan/core/serial_url.py +45 -0
  148. tan/core/setools.py +608 -0
  149. tan/core/setools_scratch.py +243 -0
  150. tan/core/shapes.py +194 -0
  151. tan/core/size.py +610 -0
  152. tan/core/som_buildability.py +204 -0
  153. tan/core/som_schema_version.py +62 -0
  154. tan/core/subprocess_env.py +201 -0
  155. tan/core/sysbuild_image.py +94 -0
  156. tan/core/system_manifest.py +827 -0
  157. tan/core/text_layout.py +73 -0
  158. tan/core/timestamp.py +182 -0
  159. tan/core/tool_lookup.py +383 -0
  160. tan/core/toolchain_pin.py +117 -0
  161. tan/core/toolchain_provision.py +1224 -0
  162. tan/core/trusted_path.py +134 -0
  163. tan/core/uboot_breakin.py +154 -0
  164. tan/core/uri_reference.py +415 -0
  165. tan/core/user_defines.py +152 -0
  166. tan/core/venv.py +332 -0
  167. tan/core/west_patches.py +143 -0
  168. tan/core/west_workspace_refusal.py +159 -0
  169. tan/core/zephyr_env.py +124 -0
  170. tan/env.py +185 -0
  171. tan/envelope.py +647 -0
  172. tan/exit_codes.py +12 -0
  173. tan/lazy_typer.py +185 -0
  174. tan/model/__init__.py +2 -0
  175. tan/model/_gen_fixture.py +132 -0
  176. tan/model/adapters/__init__.py +116 -0
  177. tan/model/adapters/cpu.py +26 -0
  178. tan/model/adapters/deepx.py +94 -0
  179. tan/model/adapters/drpai.py +242 -0
  180. tan/model/adapters/ethos_u.py +951 -0
  181. tan/model/adapters/executorch.py +55 -0
  182. tan/model/analyze.py +694 -0
  183. tan/model/build.py +304 -0
  184. tan/model/check.py +682 -0
  185. tan/model/manifest.py +625 -0
  186. tan/model/measure.py +216 -0
  187. tan/model/npu_ops_validate.py +130 -0
  188. tan/model/package.py +125 -0
  189. tan/model/perf.py +635 -0
  190. tan/model/perf_apply.py +721 -0
  191. tan/model/prep.py +193 -0
  192. tan/model/sram_fit.py +502 -0
  193. tan/model/targets.py +374 -0
  194. tan/model/tensorio.py +287 -0
  195. tan/net.py +73 -0
  196. tan/output_format.py +213 -0
  197. tan/planner/__init__.py +219 -0
  198. tan/planner/aperture.py +202 -0
  199. tan/planner/buildplan.py +1092 -0
  200. tan/planner/camera_owner.py +192 -0
  201. tan/planner/cameras.py +132 -0
  202. tan/planner/carveout.py +755 -0
  203. tan/planner/cli.py +274 -0
  204. tan/planner/headers.py +315 -0
  205. tan/planner/kconfig.py +2435 -0
  206. tan/planner/kconfig_symbols.py +473 -0
  207. tan/planner/libraries.py +677 -0
  208. tan/planner/link_target.py +237 -0
  209. tan/planner/linux_ownership.py +216 -0
  210. tan/planner/loader.py +1525 -0
  211. tan/planner/manifest.py +116 -0
  212. tan/planner/memory.py +161 -0
  213. tan/planner/memregion.py +56 -0
  214. tan/planner/models.py +533 -0
  215. tan/planner/orchestrator.py +874 -0
  216. tan/planner/ownership.py +203 -0
  217. tan/planner/partition.py +1160 -0
  218. tan/planner/paths.py +32 -0
  219. tan/planner/plain_slice.py +139 -0
  220. tan/planner/project_emit/__init__.py +60 -0
  221. tan/planner/project_emit/bom_netlist.py +375 -0
  222. tan/planner/project_emit/dts.py +580 -0
  223. tan/planner/project_emit/hw_info.py +221 -0
  224. tan/planner/project_emit/native_sim.py +136 -0
  225. tan/planner/project_emit/west_libs.py +213 -0
  226. tan/planner/project_loader.py +264 -0
  227. tan/planner/sdk_capability.py +203 -0
  228. tan/planner/sdk_compat.py +395 -0
  229. tan/planner/secure.py +350 -0
  230. tan/planner/slugs.py +394 -0
  231. tan/planner/som_metadata.py +325 -0
  232. tan/planner/strict_loaders.py +131 -0
  233. tan/planner/template.py +1037 -0
  234. tan/planner/template_pins.py +535 -0
  235. tan/planner/template_rewrite.py +714 -0
  236. tan/planner/topology.py +208 -0
  237. tan/planner/validate.py +365 -0
  238. tan/planner/whole_device_alias.py +67 -0
  239. tan/planner/zephyr_board.py +3510 -0
  240. tan/planner_cli.py +97 -0
  241. tan/planner_emit.py +617 -0
  242. tan/planner_root.py +119 -0
  243. tan/soc_ref.py +58 -0
  244. tan/templates/__init__.py +34 -0
  245. tan/templates/vendored/MANIFEST.md +1862 -0
  246. tan/templates/vendored/diagnostics/E1M-AEN801/CMakeLists.txt +8 -0
  247. tan/templates/vendored/diagnostics/E1M-AEN801/README.md +112 -0
  248. tan/templates/vendored/diagnostics/E1M-AEN801/board.yaml +38 -0
  249. tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.conf +11 -0
  250. tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.overlay +29 -0
  251. tan/templates/vendored/diagnostics/E1M-AEN801/prj.conf +5 -0
  252. tan/templates/vendored/diagnostics/E1M-AEN801/src/main.c +266 -0
  253. tan/templates/vendored/diagnostics/E1M-AEN801/testcase.yaml +53 -0
  254. tan/templates/vendored/diagnostics/E1M-V2N101/CMakeLists.txt +8 -0
  255. tan/templates/vendored/diagnostics/E1M-V2N101/README.md +112 -0
  256. tan/templates/vendored/diagnostics/E1M-V2N101/board.yaml +38 -0
  257. tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.conf +11 -0
  258. tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.overlay +29 -0
  259. tan/templates/vendored/diagnostics/E1M-V2N101/prj.conf +5 -0
  260. tan/templates/vendored/diagnostics/E1M-V2N101/src/main.c +266 -0
  261. tan/templates/vendored/diagnostics/E1M-V2N101/testcase.yaml +53 -0
  262. tan/templates/vendored/edge-ai/E1M-AEN801/CMakeLists.txt +10 -0
  263. tan/templates/vendored/edge-ai/E1M-AEN801/README.md +75 -0
  264. tan/templates/vendored/edge-ai/E1M-AEN801/board.yaml +38 -0
  265. tan/templates/vendored/edge-ai/E1M-AEN801/prj.conf +8 -0
  266. tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.c +371 -0
  267. tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.h +178 -0
  268. tan/templates/vendored/edge-ai/E1M-AEN801/src/main.c +373 -0
  269. tan/templates/vendored/edge-ai/E1M-AEN801/testcase.yaml +62 -0
  270. tan/templates/vendored/edge-ai/E1M-V2N101/CMakeLists.txt +10 -0
  271. tan/templates/vendored/edge-ai/E1M-V2N101/README.md +75 -0
  272. tan/templates/vendored/edge-ai/E1M-V2N101/board.yaml +37 -0
  273. tan/templates/vendored/edge-ai/E1M-V2N101/prj.conf +8 -0
  274. tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.c +371 -0
  275. tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.h +178 -0
  276. tan/templates/vendored/edge-ai/E1M-V2N101/src/main.c +373 -0
  277. tan/templates/vendored/edge-ai/E1M-V2N101/testcase.yaml +62 -0
  278. tan/templates/vendored/iot/E1M-AEN801/CMakeLists.txt +14 -0
  279. tan/templates/vendored/iot/E1M-AEN801/README.md +103 -0
  280. tan/templates/vendored/iot/E1M-AEN801/board.yaml +69 -0
  281. tan/templates/vendored/iot/E1M-AEN801/prj.conf +15 -0
  282. tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.c +196 -0
  283. tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.h +173 -0
  284. tan/templates/vendored/iot/E1M-AEN801/src/main.c +262 -0
  285. tan/templates/vendored/iot/E1M-AEN801/testcase.yaml +68 -0
  286. tan/templates/vendored/minimal/E1M-AEN801/CMakeLists.txt +8 -0
  287. tan/templates/vendored/minimal/E1M-AEN801/README.md +108 -0
  288. tan/templates/vendored/minimal/E1M-AEN801/board.yaml +32 -0
  289. tan/templates/vendored/minimal/E1M-AEN801/prj.conf +8 -0
  290. tan/templates/vendored/minimal/E1M-AEN801/src/main.c +111 -0
  291. tan/templates/vendored/minimal/E1M-AEN801/testcase.yaml +49 -0
  292. tan/templates/vendored/minimal/E1M-V2N101/CMakeLists.txt +8 -0
  293. tan/templates/vendored/minimal/E1M-V2N101/README.md +108 -0
  294. tan/templates/vendored/minimal/E1M-V2N101/board.yaml +32 -0
  295. tan/templates/vendored/minimal/E1M-V2N101/prj.conf +8 -0
  296. tan/templates/vendored/minimal/E1M-V2N101/src/main.c +111 -0
  297. tan/templates/vendored/minimal/E1M-V2N101/testcase.yaml +49 -0
  298. tan/templates/vendored/multicore-mailbox/E1M-AEN801/CMakeLists.txt +8 -0
  299. tan/templates/vendored/multicore-mailbox/E1M-AEN801/README.md +120 -0
  300. tan/templates/vendored/multicore-mailbox/E1M-AEN801/board.yaml +57 -0
  301. tan/templates/vendored/multicore-mailbox/E1M-AEN801/boards/native_sim_native_64.overlay +40 -0
  302. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/CMakeLists.txt +13 -0
  303. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/main.c +171 -0
  304. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/prj.conf +17 -0
  305. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/testcase.yaml +23 -0
  306. tan/templates/vendored/multicore-mailbox/E1M-AEN801/prj.conf +22 -0
  307. tan/templates/vendored/multicore-mailbox/E1M-AEN801/src/main.c +185 -0
  308. tan/templates/vendored/multicore-mailbox/E1M-AEN801/testcase.yaml +26 -0
  309. tan/templates/vendored/sensor/E1M-AEN801/CMakeLists.txt +8 -0
  310. tan/templates/vendored/sensor/E1M-AEN801/README.md +112 -0
  311. tan/templates/vendored/sensor/E1M-AEN801/board.yaml +57 -0
  312. tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.conf +10 -0
  313. tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.overlay +28 -0
  314. tan/templates/vendored/sensor/E1M-AEN801/prj.conf +5 -0
  315. tan/templates/vendored/sensor/E1M-AEN801/src/main.c +186 -0
  316. tan/templates/vendored/sensor/E1M-AEN801/testcase.yaml +52 -0
  317. tan/templates/vendored/sensor/E1M-V2N101/CMakeLists.txt +8 -0
  318. tan/templates/vendored/sensor/E1M-V2N101/README.md +112 -0
  319. tan/templates/vendored/sensor/E1M-V2N101/board.yaml +57 -0
  320. tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.conf +10 -0
  321. tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.overlay +28 -0
  322. tan/templates/vendored/sensor/E1M-V2N101/prj.conf +5 -0
  323. tan/templates/vendored/sensor/E1M-V2N101/src/main.c +186 -0
  324. tan/templates/vendored/sensor/E1M-V2N101/testcase.yaml +52 -0
  325. tan/version.py +102 -0
  326. tan_cli-0.7.0.dist-info/METADATA +125 -0
  327. tan_cli-0.7.0.dist-info/RECORD +330 -0
  328. tan_cli-0.7.0.dist-info/WHEEL +5 -0
  329. tan_cli-0.7.0.dist-info/entry_points.txt +2 -0
  330. tan_cli-0.7.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,196 @@
1
+ /*
2
+ * SPDX-License-Identifier: Apache-2.0
3
+ * Copyright 2026 Alp Lab AB
4
+ *
5
+ * E1M-AEN SoM CC3501E bridge bring-up helper -- see cc3501e_bridge.h.
6
+ *
7
+ * The final reset is warm-aware. cc3501e_reset() drops WIFI_EN for 50 ms, a
8
+ * supply cold cycle; on a CC3501E that is already running (an activated unit with
9
+ * vendor_sbl_container_enable=1 may then never relaunch) that must not happen on a
10
+ * mere re-run or warm reset of the application. So when WIFI_EN already reads high
11
+ * AND the chip answers a PING the bring-up resets through nRESET only
12
+ * (cc3501e_hard_reset()), and uses cc3501e_reset() only from WIFI_EN low or, as a
13
+ * last resort, from a chip that stays silent through an nRESET-only reset. WIFI_EN
14
+ * reading high alone is not trusted: a pull-up would make every power-on look warm
15
+ * (whether the module has one is an open question, issue #2797).
16
+ */
17
+
18
+ #include "cc3501e_bridge.h"
19
+
20
+ #include <stdio.h>
21
+
22
+ #if defined(CONFIG_SOC_AE822FA0E5597LS0_RTSS_HE) || defined(CONFIG_SOC_AE822FA0E5597LS0_RTSS_HP)
23
+ #include <zephyr/arch/cpu.h>
24
+ #include <zephyr/devicetree.h>
25
+ #include <zephyr/sys/sys_io.h>
26
+ /*
27
+ * AEN LP-pad mux (Alif Ensemble E8, both M55-HE and M55-HP cores). WIFI_EN
28
+ * (P15_5) and nRESET (P15_1) are on the Alif LP-GPIO island, bound by the
29
+ * generic snps,designware-gpio driver which does NOT apply Alif pinctrl --
30
+ * so the LP pads stay un-muxed and their output drivers OFF (confirmed on
31
+ * silicon on M55-HE: WIFI_EN never powers the CC3501E until these regs are
32
+ * set). 0x23 = the Alif GPIO-output pad config (driver + read-enable +
33
+ * drive strength). TODO: drop this raw poke once the Alif GPIO backend
34
+ * muxes the LP island via pinctrl.
35
+ *
36
+ * ALIF_LPGPIO_PADCTRL_BASE (0x42007000) is not an M55-HE-local address: the
37
+ * shared upstream `pin-controller@1a603000` node (ensemble_common.dtsi,
38
+ * included by both cores' SoC dtsi) declares this exact window as its
39
+ * second `reg` range, and `alp_e1m_aen801_m55_hp-pinctrl.dtsi` already
40
+ * muxes a different LP-GPIO pad (P15_0, RTC_ALARM) from the M55-HP pinctrl
41
+ * driver through it, so the poke below is equally applicable on M55-HP.
42
+ * Running it on M55-HP is NOT bench-proven -- only running it on M55-HE has
43
+ * been confirmed on silicon.
44
+ */
45
+ #define ALIF_LPGPIO_PADCTRL_BASE 0x42007000u
46
+ #define ALIF_PAD_GPIO_OUTPUT 0x23u
47
+ /* Latch WIFI_EN (P15_5) HIGH in the port's data register while the pad is still an
48
+ * INPUT. Switching the direction afterwards then drives the already-high level; the
49
+ * other order (direction first, value second -- what gpio_dw's configure does even
50
+ * with an init flag) drives whatever DR holds, and with DR bit 5 = 0 that glitches
51
+ * the supply of a running chip low. DR is written directly (read-modify-write, the
52
+ * same access gpio_dw's port_set_bits_raw makes) so this template stays on the
53
+ * portable <alp/...> headers: SWPORTA_DR is offset 0x00 of the DesignWare GPIO block
54
+ * (zephyr drivers/gpio/gpio_dw_registers.h), the block base is the `lpgpio` node's
55
+ * reg, and the LP-GPIO clock is already on because alp_gpio_open() opened the pin. */
56
+ #define DW_GPIO_SWPORTA_DR 0x00u
57
+ static void aen_wifi_en_latch_high(void)
58
+ {
59
+ const mem_addr_t dr = DT_REG_ADDR(DT_NODELABEL(lpgpio)) + DW_GPIO_SWPORTA_DR;
60
+
61
+ sys_write32(sys_read32(dr) | (1u << 5), dr);
62
+ }
63
+
64
+ static void aen_lp_pads_enable_output(void)
65
+ {
66
+ sys_write32(ALIF_PAD_GPIO_OUTPUT, ALIF_LPGPIO_PADCTRL_BASE + 5u * 4u); /* P15_5 WIFI_EN */
67
+ sys_write32(ALIF_PAD_GPIO_OUTPUT, ALIF_LPGPIO_PADCTRL_BASE + 1u * 4u); /* P15_1 nRESET */
68
+ }
69
+ #else
70
+ static inline void aen_lp_pads_enable_output(void)
71
+ {
72
+ }
73
+ static inline void aen_wifi_en_latch_high(void)
74
+ {
75
+ }
76
+ #endif
77
+
78
+ alp_status_t cc3501e_bridge_bringup(cc3501e_t *fw)
79
+ {
80
+ if (fw == NULL) {
81
+ return ALP_ERR_INVAL;
82
+ }
83
+
84
+ /* 1. Control pins: WIFI_EN (supply gate) + nRESET. These are Alif LP-GPIO
85
+ * pads, not E1M edge pads -- the SoM owns them. */
86
+ alp_gpio_t *wifi_en = alp_gpio_open(CC3501E_BRIDGE_PIN_WIFI_EN);
87
+ alp_gpio_t *nrst = alp_gpio_open(CC3501E_BRIDGE_PIN_NRST);
88
+ if (wifi_en == NULL || nrst == NULL) {
89
+ if (wifi_en != NULL) {
90
+ alp_gpio_close(wifi_en);
91
+ }
92
+ if (nrst != NULL) {
93
+ alp_gpio_close(nrst);
94
+ }
95
+ return ALP_ERR_NOT_PRESENT_ON_THIS_SOC;
96
+ }
97
+ aen_lp_pads_enable_output();
98
+ /* Sample the supply gate BEFORE touching its direction. High is only a HINT
99
+ * that the chip may be powered -- an external pull-up on WIFI_EN would make every
100
+ * power-on read high (whether the module has one is an OPEN QUESTION; the
101
+ * netlist has not been checked), so the decision below also needs a PING. */
102
+ bool wifi_was_high = false;
103
+ /* A failed read leaves the level unknown: never take the supply-cycling cold path
104
+ * on that alone -- go through the PING path below, which still ends in
105
+ * cc3501e_reset() if the chip turns out to be silent. */
106
+ const bool wifi_unknown = alp_gpio_read(wifi_en, &wifi_was_high) != ALP_OK;
107
+ if (wifi_was_high) {
108
+ /* Latch the level in DR first so the direction change cannot glitch it low. */
109
+ aen_wifi_en_latch_high();
110
+ }
111
+ (void)alp_gpio_configure(wifi_en, ALP_GPIO_OUTPUT, ALP_GPIO_PULL_NONE);
112
+ (void)alp_gpio_configure(nrst, ALP_GPIO_OUTPUT, ALP_GPIO_PULL_NONE);
113
+
114
+ /* 2. Inter-chip SPI (Alif = master). CS is the dwc-ssi hardware SS0
115
+ * muxed on P14_7; the app passes ALP_SPI_NO_CS so no software GPIO CS
116
+ * is installed and the SPI controller drives SS0 per transfer. Mode 0
117
+ * matches the CC3501E vendor image frameFormat. */
118
+ alp_spi_t *spi = alp_spi_open(&(alp_spi_config_t){
119
+ .bus_id = CC3501E_BRIDGE_SPI_BUS_ID,
120
+ .freq_hz = CC3501E_BRIDGE_SPI_FREQ_HZ,
121
+ .mode = ALP_SPI_MODE_0,
122
+ .bits_per_word = 8u,
123
+ .cs_pin_id = ALP_SPI_NO_CS,
124
+ });
125
+ if (spi == NULL) {
126
+ alp_gpio_close(wifi_en);
127
+ alp_gpio_close(nrst);
128
+ return ALP_ERR_NOT_PRESENT_ON_THIS_SOC;
129
+ }
130
+
131
+ #if CC3501E_BRIDGE_RX_SAMPLE_DLY > 0
132
+ /* Raise the SCLK past 1 MHz: the DW SSI RX_SAMPLE_DLY register (0xf0) delays
133
+ * the MISO capture point by N ssi_clk cycles to cover the on-SoM trace + crossed
134
+ * data round-trip. Zephyr spi_dw never writes it (leaves 0), so without this
135
+ * >1 MHz mis-samples MISO (cold reqhdr_rx=0xFFFFFFFF). Written with SSI disabled
136
+ * (SSIENR=0); persists across the driver's per-transfer configure. Value is
137
+ * silicon-tuned -- sweep CC3501E_BRIDGE_RX_SAMPLE_DLY at the target SCLK.
138
+ *
139
+ * SWEEP TRAP -- read before setting the constant to 0: the `#if > 0` above
140
+ * compiles this block out, so 0 does not mean "no delay", it means the SPI
141
+ * node's `rx-delay` devicetree value is left in place. aen-cc3501e-bringup
142
+ * and aen-evk-demo declare rx-delay = <2>, so a naive N=0 sweep point on
143
+ * those two silently measures 2. Read 0x481040F0 back to see what the link
144
+ * is actually running at. */
145
+ {
146
+ volatile uint32_t *ssienr =
147
+ (volatile uint32_t *)(uintptr_t)(CC3501E_BRIDGE_SPI1_BASE + 0x08u);
148
+ volatile uint32_t *rsd = (volatile uint32_t *)(uintptr_t)(CC3501E_BRIDGE_SPI1_BASE + 0xf0u);
149
+ uint32_t en = *ssienr;
150
+ *ssienr = 0u;
151
+ *rsd = (uint32_t)CC3501E_BRIDGE_RX_SAMPLE_DLY;
152
+ *ssienr = en;
153
+ }
154
+ #endif
155
+
156
+ /* 3. Bind the bus + control pins; attach the GPIO proxy (proxied E1M IOs then
157
+ * route over the bridge); run the power + reset sequence (TI SWRU626 + the
158
+ * Puya cold-boot hard-reset workaround). Leaves WIFI_EN HIGH. */
159
+ (void)cc3501e_init(fw, spi);
160
+ fw->enable_pin = wifi_en;
161
+ fw->reset_pin = nrst;
162
+ /* READY (CC35 GPIO17) is NOT wired here by default on this R2 module --
163
+ * see chips/cc3501e/cc3501e_core.c's cc3501e_reply_gate() comment for the
164
+ * pin-routing fact, the bench evidence, and how a board that genuinely
165
+ * wires it can opt back in via fw->ready_pin. */
166
+ #ifdef CONFIG_ALP_SDK_GPIO_CC3501E_PROXY
167
+ (void)alp_gpio_cc3501e_attach(fw);
168
+ #endif
169
+ #ifdef CONFIG_ALP_SDK_WIFI_CC3501E
170
+ (void)alp_wifi_cc3501e_attach(fw);
171
+ #endif
172
+ #ifdef CONFIG_ALP_SDK_BLE_CC3501E
173
+ (void)alp_ble_cc3501e_attach(fw);
174
+ #endif
175
+ /* One line naming the path taken, so a log shows whether the supply was cycled. */
176
+ if (!wifi_was_high && !wifi_unknown) {
177
+ printf("[cc3501e_bridge] WIFI_EN low: cold power-up (cc3501e_reset)\n");
178
+ return cc3501e_reset(fw); /* supply low: the full cold-boot sequence */
179
+ }
180
+ /* WIFI_EN reads high. Warm = the chip ANSWERS a PING; a pull-up alone proves
181
+ * nothing. Warm: nRESET only, never a WIFI_EN toggle. */
182
+ if (cc3501e_ping(fw) == ALP_OK) {
183
+ printf("[cc3501e_bridge] WIFI_EN high, PING ok: warm, nRESET only\n");
184
+ return cc3501e_hard_reset(fw);
185
+ }
186
+ /* High but silent: powered-and-hung, or unpowered behind a pull-up. Try the
187
+ * nRESET-only reset first (safe for a powered chip); only if the chip still does
188
+ * not answer is the supply cycled -- the last resort, taken from a chip that is
189
+ * already unresponsive. */
190
+ if (cc3501e_hard_reset(fw) == ALP_OK && cc3501e_ping(fw) == ALP_OK) {
191
+ printf("[cc3501e_bridge] WIFI_EN high, silent: recovered by nRESET only\n");
192
+ return ALP_OK;
193
+ }
194
+ printf("[cc3501e_bridge] WIFI_EN high, silent after nRESET: supply cycle (cc3501e_reset)\n");
195
+ return cc3501e_reset(fw);
196
+ }
@@ -0,0 +1,173 @@
1
+ /*
2
+ * SPDX-License-Identifier: Apache-2.0
3
+ * Copyright 2026 Alp Lab AB
4
+ *
5
+ * One-call bring-up of the E1M-AEN SoM's CC3501E Wi-Fi 6 / BLE coprocessor over the
6
+ * inter-chip SPI bridge -- the SoM bring-up TEMPLATE for applications.
7
+ *
8
+ * The CC3501E is part of the SoM (module U4 = BDE-BW35N): the application does NOT
9
+ * touch the raw SPI bus or the WIFI_EN / nRESET control pins. It calls
10
+ * cc3501e_bridge_bringup() once, gets a ready @ref cc3501e_t, and from there uses the
11
+ * portable surfaces:
12
+ * - cc3501e_* (chips/cc3501e) -- MAC / Wi-Fi / BLE / GPIO-proxy / OTA
13
+ * - alp_gpio_open(ALP_E1M_GPIO_IOxx) -- proxied E1M IOs (when the proxy is built)
14
+ *
15
+ * To reuse in your own AEN application: copy this pair (cc3501e_bridge.{c,h}) into your
16
+ * app, or call cc3501e_bridge_bringup() directly. The bus / pins / clock are the
17
+ * E1M-AEN SoM defaults below; a board variant overrides only those macros -- the SoM
18
+ * stays swappable without touching application code.
19
+ */
20
+
21
+ #ifndef CC3501E_BRIDGE_H
22
+ #define CC3501E_BRIDGE_H
23
+
24
+ #include <alp/peripheral.h> /* alp_status_t */
25
+ #include <alp/chips/cc3501e.h> /* cc3501e_t */
26
+
27
+ /* ---- E1M-AEN SoM bridge defaults (override per board variant) ---------------- */
28
+
29
+ /* Inter-chip SPI: Alif = master, CC3501E = slave. P14_7 is muxed as the
30
+ * dwc-ssi hardware SS0 chip-select; ALP_SPI_NO_CS means "no software GPIO CS"
31
+ * here, so the controller frames each protocol phase. Mode 0 matches the
32
+ * CC3501E vendor image frameFormat. */
33
+ #ifndef CC3501E_BRIDGE_SPI_BUS_ID
34
+ #define CC3501E_BRIDGE_SPI_BUS_ID 1u
35
+ #endif
36
+ #ifndef CC3501E_BRIDGE_SPI_FREQ_HZ
37
+ /* 25 MHz = 200 MHz SSI functional clock / 8.
38
+ *
39
+ * The CC3501E peripheral-mode maximum is 30 MHz, NOT the 15 MHz this comment
40
+ * claimed for most of the bring-up: datasheet SWRS343A section 6.14.2.3.3,
41
+ * `fsclk SPI clock frequency, Peripheral Mode, MAX 30 MHz` (Controller Mode is
42
+ * 40 MHz). Every earlier ceiling argument here was built on the wrong number.
43
+ *
44
+ * 25 MHz is the fastest LEGAL step available: the DW SSI BAUDR divisor must be
45
+ * EVEN, so from 200 MHz the neighbours are /8 = 25 MHz and /6 = 33.3 MHz, and
46
+ * 33.3 is 11% over the part's maximum. Reaching exactly 30 MHz would require
47
+ * retargeting the SSI functional clock itself (AE822 HWRM section 8.3.5) --
48
+ * ALIF_SPI_CLK is a frequency-only dummy in the dtsi with no divider control,
49
+ * so there is no software knob for it here.
50
+ *
51
+ * REQUIRES an RX sample delay: at a 40 ns bit period the MISO capture does not
52
+ * land on its own. That delay comes from CC3501E_BRIDGE_RX_SAMPLE_DLY below --
53
+ * NOT from the SPI node's `rx-delay` property, which the poke in
54
+ * cc3501e_bridge.c overwrites. The measured working span is in that constant's
55
+ * comment.
56
+ *
57
+ * It does not raise throughput -- silicon-measured
58
+ * 2026-08-24: 682 kB/s at 25 MHz vs 678 kB/s at 14.29 MHz on PIO, 704 vs 701 on
59
+ * DMA, because wire time is only ~554 us of a ~2459 us transaction and the rest
60
+ * is per-frame protocol cost on both ends. It is worth taking anyway: each
61
+ * transfer occupies the bus for ~44% less time, which is CPU and bus budget
62
+ * handed back to everything else on the SoM.
63
+ *
64
+ * The 14.29 MHz predecessor was scope-confirmed at 14.20-14.26 MHz, which is
65
+ * what validates the 200 MHz functional-clock figure the divider assumes. A
66
+ * board with longer traces should re-check MISO capture before adopting 25 MHz.
67
+ *
68
+ * SEPARATE, PRE-EXISTING DEFECT (do not blame this clock for it): a bulk read
69
+ * intermittently desyncs partway through -- roughly 1 run in 2-3, at anywhere
70
+ * from 61 kB to 212 kB in -- and then never recovers for the caller's whole
71
+ * retry budget. It reproduces at 14.29 MHz TOO, so it is not a clock artifact.
72
+ * Signature (host-side, request-header phase of CMD_SOCK_RECV 0x23): the
73
+ * slave's in-band idle marker reads [02 00 00 00] once and then [00 00 00 00]
74
+ * forever instead of A5 A5 A5 A5 -- i.e. the slave is stuck mid-reply on a
75
+ * transfer it armed earlier and never completed. With
76
+ * SPI_TRANSFER_RETURN_PARTIAL deliberately off, a CS deassert does not complete
77
+ * it, and NEITHER slave self-heal fires (g_resync_count does not move -- the
78
+ * slave is not misframing; g_arm_fail_count does not move -- the arm succeeded).
79
+ * Clocking a full idle frame from the host to satisfy the outstanding count was
80
+ * tried and did NOT recover it. The fix likely belongs on the slave: a
81
+ * stall watchdog that calls bridge_transport_spi_hw_reinit() when an armed
82
+ * transfer has not completed for N ms. */
83
+ #define CC3501E_BRIDGE_SPI_FREQ_HZ 25000000u
84
+ #endif
85
+
86
+ /* CC3501E control pins on the Alif LP-GPIO island (NOT E1M edge pads):
87
+ * WIFI_EN = supply gate (P15_5), nRESET = reset (P15_1_FLEX). */
88
+ #ifndef CC3501E_BRIDGE_PIN_WIFI_EN
89
+ #define CC3501E_BRIDGE_PIN_WIFI_EN 0u
90
+ #endif
91
+ #ifndef CC3501E_BRIDGE_PIN_NRST
92
+ #define CC3501E_BRIDGE_PIN_NRST 1u
93
+ #endif
94
+ /* OPTIONAL host-IRQ/READY input. ready_pin has no fixed index or macro
95
+ * here on purpose: on a board where the CC3501E's GPIO17 READY net is
96
+ * actually routed to the host, set fw->ready_pin (in
97
+ * cc3501e_bridge_bringup() below) to whatever Alif pin that board wires it
98
+ * to. When set, cc3501e_reply_gate() (chips/cc3501e/cc3501e_core.c) can
99
+ * only ever ADD delay to a reply phase, never remove it, so opting in on a
100
+ * board where the wiring is wrong costs up to 3 x 250 ms of busy-wait per
101
+ * latch cycle before the driver's own (recoverable) stuck-LOW latch gives up
102
+ * on it -- longer thresholds apply after each re-latch if the line flaps
103
+ * instead of staying cleanly stuck (chips/cc3501e/cc3501e_core.c's
104
+ * g_ready_stuck_low_threshold) -- it is always safe to try FOR LINK TIMING.
105
+ * It is not free, though: this
106
+ * wait runs while cc3501e_request() holds its internal transport lock, so
107
+ * a wrong or slow ready_pin can make another caller on the same ctx see
108
+ * ALP_ERR_BUSY after CONFIG_ALP_SDK_CC3501E_REQUEST_LOCK_TIMEOUT_MS -- see
109
+ * <alp/chips/cc3501e/core.h>'s ready_pin doc.
110
+ *
111
+ * NOT Alif P2_6 on an R2 AEN EVK bench module: P2_6 there is E1M pad AH7 /
112
+ * I2S1_SCLK (the EVK's Arduino CK_RST, metadata/boards/e1m-evk.yaml), and
113
+ * the CC3501E GPIO17 READY net lands on E1M pad G3 / IO16 instead
114
+ * (metadata/e1m_modules/aen/from-cc3501e.tsv). cc3501e_bridge_bringup()
115
+ * below leaves ready_pin NULL by default on that module -- see
116
+ * cc3501e_reply_gate()'s own doc comment for the bench evidence. */
117
+
118
+ /* DW SSI SPI1 base (0x48104000) + RX_SAMPLE_DLY to run the bridge SCLK above
119
+ * 1 MHz. spi_dw_configure never writes RX_SAMPLE_DLY (0xf0), so without the
120
+ * poke in cc3501e_bridge.c the master samples MISO at the SCLK edge, before the
121
+ * on-SoM trace + crossed-data round-trip returns the CC35's bit, and anything
122
+ * above 1 MHz mis-samples. Setting it delays the capture by N ssi_clk
123
+ * (200 MHz) cycles -- 5 ns per step.
124
+ *
125
+ * 4 is silicon-measured at the 25 MHz working point (40 ns bit period) on
126
+ * E1M-AEN803 serial 2026W36-0002. N was swept 0..10 with 0x481040F0 read back
127
+ * each time to confirm the value took, using aen-evk-demo phase 8 as the
128
+ * workload:
129
+ *
130
+ * N = 0..7 PASS, 2-3 runs each: bring-up 0, PING on attempt 1 of 25,
131
+ * byte-exact MAC, scan 0, BLE 0.
132
+ * N = 8 FAIL, 3 runs of 3: bring-up still returns 0, but the link never
133
+ * answers -- PING -> -5 after all 25 attempts.
134
+ * N = 9 PASS, 2 runs of 2.
135
+ * N = 10 PASS, 3 runs of 3, but one run returned a MAC with a single bit
136
+ * flipped in byte 0.
137
+ *
138
+ * 4 is the centre of the measured-good 0..7 span: +4 steps (20 ns, half a bit
139
+ * period) below the failure, and the low side is not a cliff at all since 0
140
+ * itself is clean. Do NOT read 8/9/10 as a monotone upper boundary -- 8
141
+ * hard-fails while 9 and 10 pass, which this sweep did not explain. Treat
142
+ * everything above 7 as unqualified, not as graded margin.
143
+ *
144
+ * The "4..8 all clean cold+warm" window this comment used to claim was taken on
145
+ * an earlier bench run at ~14.3 MHz -- a different board at roughly half the clock,
146
+ * so it is not comparable and is not evidence for 25 MHz on this one.
147
+ *
148
+ * 0 does NOT mean "no delay": it compiles the poke out, leaving whatever the
149
+ * SPI node's `rx-delay` property put there (see cc3501e_bridge.c). Re-sweep if
150
+ * the SoM trace lengths or the SCLK change. */
151
+ #ifndef CC3501E_BRIDGE_SPI1_BASE
152
+ #define CC3501E_BRIDGE_SPI1_BASE 0x48104000u
153
+ #endif
154
+ #ifndef CC3501E_BRIDGE_RX_SAMPLE_DLY
155
+ #define CC3501E_BRIDGE_RX_SAMPLE_DLY 4u
156
+ #endif
157
+
158
+ /**
159
+ * @brief Bring up the SoM's CC3501E coprocessor over the inter-chip bridge.
160
+ *
161
+ * Opens the hardware-SS0 bridge SPI + the WIFI_EN / nRESET control pins, binds
162
+ * them to @p fw, attaches the GPIO proxy (when CONFIG_ALP_SDK_GPIO_CC3501E_PROXY is
163
+ * built), and runs the power + reset sequence (TI SWRU626 + the Puya cold-boot
164
+ * hard-reset workaround). Blocks ~900 ms for the boot budget; leaves WIFI_EN HIGH.
165
+ *
166
+ * @param fw Caller-owned handle, populated on success. Use it with cc3501e_*.
167
+ * @return ALP_OK with @p fw ready; ALP_ERR_NOT_PRESENT_ON_THIS_SOC if the SPI bus /
168
+ * control pins are absent (check the board overlay); otherwise the reset
169
+ * sequence status. On any failure @p fw is left un-bound (do not use it).
170
+ */
171
+ alp_status_t cc3501e_bridge_bringup(cc3501e_t *fw);
172
+
173
+ #endif /* CC3501E_BRIDGE_H */
@@ -0,0 +1,262 @@
1
+ /*
2
+ * Copyright 2026 Alp Lab AB
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ *
5
+ * mqtt-telemetry -- publish a sensor reading over MQTT/TLS.
6
+ *
7
+ * The "connected device" starting point. It shows the whole
8
+ * telemetry path a field device runs, all on the portable
9
+ * <alp/iot.h> surface:
10
+ *
11
+ * 1. Bring the Wi-Fi station up + associate. (alp_wifi_*)
12
+ * 2. Open an mqtts:// (TLS) MQTT client. (alp_mqtt_open)
13
+ * 3. Connect to the broker. (alp_mqtt_connect)
14
+ * 4. Publish a telemetry reading on a cadence. (alp_mqtt_publish)
15
+ * 5. Disconnect cleanly. (alp_mqtt_close)
16
+ *
17
+ * Transport (E1M-AEN801): the CC3501E Wi-Fi6+BLE coprocessor bridge
18
+ * (https://github.com/alplabai/alp-sdk/blob/v0.17.0/docs/cc3501e-bridge.md), selected automatically by the AEN board
19
+ * emit from `iot.wifi: true` -- the app never names it, it just
20
+ * calls <alp/iot.h>. The bridge is silicon-validated (2026-06-24);
21
+ * this is a real transport, not a stub.
22
+ *
23
+ * Bridge bring-up (#2112): alp_wifi_open() only resolves to the CC3501E
24
+ * backend once an application has attached a live bridge handle --
25
+ * cc3501e_bridge_bringup() (src/cc3501e_bridge.c, the same copyable
26
+ * template every https://github.com/alplabai/alp-sdk/tree/v0.17.0/examples/aen/aen-cc3501e-* app uses) does exactly that
27
+ * before this app calls alp_wifi_open() below. On native_sim the bring-up
28
+ * fails fast (ALP_ERR_NOT_PRESENT_ON_THIS_SOC -- no SPI/GPIO backend
29
+ * matches that SoC ref) and this app falls through to the same
30
+ * NULL-with-NOT_READY framing path as before.
31
+ *
32
+ * The "sensor reading": to keep the focus on the transport, this
33
+ * template publishes a synthetic metric (device uptime). Swap
34
+ * read_telemetry_value() for a real sensor read -- e.g. compose it
35
+ * with the `sensor` template (https://github.com/alplabai/alp-sdk/tree/v0.17.0/examples/peripheral-io/i2c-master,
36
+ * TMP112 over <alp/chips/tmp112.h>) -- and the publish path here is
37
+ * unchanged.
38
+ *
39
+ * TLS status (preview): the mqtts:// path is configured through the
40
+ * portable API (broker URI + pinned CA), but CONFIG_MBEDTLS is held
41
+ * OFF in the build today -- Zephyr v4.4's mbedtls 3.6 has an
42
+ * ssl_misc.h include-order bug; full tf-psa-crypto wiring is a v0.6
43
+ * work item (see prj.conf / native_sim.conf). Real AEN silicon
44
+ * uses the TF-M stack. This is why the catalog record is `preview`.
45
+ *
46
+ * What runs under native_sim (CI lane): there is no radio and no
47
+ * MQTT-capable net stack, so alp_wifi_open()/alp_mqtt_open() return
48
+ * NULL -- ALP_ERR_NOT_READY when a backend registered but its device
49
+ * isn't up, or ALP_ERR_NOSUPPORT when no backend is linked at all;
50
+ * both are the documented <alp/iot.h> NULL contract. The app
51
+ * detects that, prints the telemetry payloads it WOULD publish (so
52
+ * the framing is CI-observable), and exits on the `[mqtt] done`
53
+ * marker.
54
+ *
55
+ * What runs on AEN HiL: real association through the CC3501E, real
56
+ * MQTT publish to the broker; the same source, no #ifdef in the
57
+ * telemetry logic.
58
+ */
59
+
60
+ #include <stdint.h>
61
+ #include <stdio.h>
62
+ #include <string.h>
63
+
64
+ #include "alp/iot.h"
65
+ #include "cc3501e_bridge.h" /* cc3501e_bridge_bringup() -- the SoM bring-up template, #2112 */
66
+
67
+ /* Broker + topic identity. In a real fleet these come from the
68
+ * device's provisioning step; hardcoded here as documentation.
69
+ * mqtts:// selects the TLS path (see the TLS config below). */
70
+ #define MQTT_BROKER_URI "mqtts://broker.example.alplab.ai:8883"
71
+ #define MQTT_CLIENT_ID "e1m-aen801-demo"
72
+ #define MQTT_TOPIC "alp/telemetry/e1m-aen801"
73
+
74
+ /* Pin the broker's CA bundle. NULL would fall back to the OS
75
+ * default trust store; a real deployment should pin a known-good
76
+ * CA, so we name the path a customer would provision. */
77
+ #define MQTT_CA_FILE "/etc/ssl/certs/alp-broker-ca.pem"
78
+
79
+ /* Wi-Fi credentials. On real silicon these come from the EEPROM
80
+ * factory manifest or a provisioning blob so one binary serves a
81
+ * whole fleet; hardcoded for the demo. */
82
+ #define WIFI_SSID "alp-demo-ap"
83
+ #define WIFI_PSK "changeme-in-provisioning"
84
+
85
+ /* Publish a bounded number of readings so the native_sim run
86
+ * terminates and the twister harness latches. Real firmware would
87
+ * publish forever on a timer. */
88
+ #define PUBLISH_COUNT 3u
89
+ #define PUBLISH_PERIOD_S 5u
90
+
91
+ /* Handshake budgets.
92
+ *
93
+ * WIFI_CONNECT_TIMEOUT_MS: on E1M-AEN801 with the CC3501E bridge attached,
94
+ * alp_wifi_connect() routes to the CC3501E backend
95
+ * (src/backends/wifi/cc3501e.c), and the bridge's own worst case for one STA
96
+ * connect is 10s Wlan_RoleUp + 30s L2 association + a 30s DHCP-lease poll
97
+ * (hal/ti/cc3501e_hw_ti_wifi.c) = 70s. On other Wi-Fi backends this value is
98
+ * only an upper bound, not a derived worst case. */
99
+ #define WIFI_CONNECT_TIMEOUT_MS 75000u
100
+ #define MQTT_CONNECT_TIMEOUT_MS 10000u
101
+
102
+ /* Read one telemetry value.
103
+ *
104
+ * PLACEHOLDER: returns a synthetic uptime-seconds counter so the
105
+ * template stays chip-free and focused on the transport. On real
106
+ * hardware, replace the body with a sensor read -- e.g. a TMP112
107
+ * temperature via <alp/chips/tmp112.h> (see the `sensor` template)
108
+ * -- and return that value. The publish path below does not change. */
109
+ static uint32_t read_telemetry_value(uint32_t sample_index)
110
+ {
111
+ /* Synthetic: seconds of "uptime" at this sample's cadence.
112
+ * Deterministic so the native_sim framing output is stable. */
113
+ return sample_index * PUBLISH_PERIOD_S;
114
+ }
115
+
116
+ /* Format a telemetry sample as a compact JSON payload. Returns the
117
+ * byte length written (excluding the NUL), or 0 on truncation. */
118
+ static size_t build_payload(char *buf, size_t cap, uint32_t value)
119
+ {
120
+ int n = snprintf(buf,
121
+ cap,
122
+ "{\"device\":\"%s\",\"metric\":\"uptime_s\",\"value\":%u}",
123
+ MQTT_CLIENT_ID,
124
+ value);
125
+ if (n < 0 || (size_t)n >= cap) {
126
+ return 0u; /* truncated -- caller skips the publish */
127
+ }
128
+ return (size_t)n;
129
+ }
130
+
131
+ /* Bring the Wi-Fi station up + associate. Returns an open handle
132
+ * on success, or NULL (with the reason logged) when there is no
133
+ * Wi-Fi backend on this build -- the native_sim / sw_fallback path. */
134
+ static alp_wifi_t *telemetry_wifi_up(void)
135
+ {
136
+ /* STATIC, not a local -- cc3501e_t embeds several
137
+ * ALP_CC3501E_MAX_PAYLOAD scratch buffers; see cc3501e_bridge.h. */
138
+ static cc3501e_t bridge = { 0 };
139
+
140
+ printf("[mqtt] wifi: bridge bring-up\n");
141
+ alp_status_t bridge_rc = cc3501e_bridge_bringup(&bridge);
142
+ printf("[mqtt] wifi: cc3501e_bridge_bringup -> %s\n", alp_status_name(bridge_rc));
143
+
144
+ printf("[mqtt] wifi: opening station\n");
145
+ alp_wifi_t *w = alp_wifi_open();
146
+ if (w == NULL) {
147
+ /* NOT_READY on native_sim (a Wi-Fi backend registered but no
148
+ * device is up) or NOSUPPORT when no Wi-Fi backend is linked;
149
+ * on HiL, NOT_READY before the CC3501E bridge has probed. */
150
+ printf("[mqtt] wifi: alp_wifi_open -> NULL (%s)\n", alp_status_name(alp_last_error()));
151
+ return NULL;
152
+ }
153
+ const alp_wifi_credentials_t creds = {
154
+ .ssid = WIFI_SSID,
155
+ .psk = WIFI_PSK,
156
+ };
157
+ alp_status_t s = alp_wifi_connect(w, &creds, WIFI_CONNECT_TIMEOUT_MS);
158
+ if (s != ALP_OK) {
159
+ printf("[mqtt] wifi: connect -> %s\n", alp_status_name(s));
160
+ alp_wifi_close(w);
161
+ return NULL;
162
+ }
163
+ printf("[mqtt] wifi: associated to \"%s\"\n", WIFI_SSID);
164
+ return w;
165
+ }
166
+
167
+ /* Open + connect the TLS MQTT client. Returns an open handle, or
168
+ * NULL when there is no MQTT backend on this build. */
169
+ static alp_mqtt_t *telemetry_mqtt_up(void)
170
+ {
171
+ printf("[mqtt] broker: opening %s (TLS, CA pinned)\n", MQTT_BROKER_URI);
172
+
173
+ /* Pin the broker CA for the mqtts:// handshake. insecure
174
+ * stays false -- a starter must not teach cert-skipping. */
175
+ static const alp_mqtt_tls_config_t tls = {
176
+ .ca_file = MQTT_CA_FILE,
177
+ .insecure = false,
178
+ };
179
+ alp_mqtt_config_t cfg = ALP_MQTT_CONFIG_DEFAULT(MQTT_BROKER_URI);
180
+ cfg.client_id = MQTT_CLIENT_ID;
181
+ cfg.tls = &tls;
182
+
183
+ alp_mqtt_t *m = alp_mqtt_open(&cfg);
184
+ if (m == NULL) {
185
+ printf("[mqtt] broker: alp_mqtt_open -> NULL (%s)\n", alp_status_name(alp_last_error()));
186
+ return NULL;
187
+ }
188
+ alp_status_t s = alp_mqtt_connect(m, MQTT_CONNECT_TIMEOUT_MS);
189
+ if (s != ALP_OK) {
190
+ printf("[mqtt] broker: connect -> %s\n", alp_status_name(s));
191
+ alp_mqtt_close(m);
192
+ return NULL;
193
+ }
194
+ printf("[mqtt] broker: connected\n");
195
+ return m;
196
+ }
197
+
198
+ /* Publish PUBLISH_COUNT readings on the live client. */
199
+ static void telemetry_publish_loop(alp_mqtt_t *m)
200
+ {
201
+ for (uint32_t i = 0; i < PUBLISH_COUNT; i++) {
202
+ char payload[96];
203
+ size_t len = build_payload(payload, sizeof(payload), read_telemetry_value(i));
204
+ if (len == 0u) {
205
+ printf("[mqtt] publish %u: payload truncated -- skipped\n", i);
206
+ continue;
207
+ }
208
+ alp_status_t s =
209
+ alp_mqtt_publish(m, MQTT_TOPIC, (const uint8_t *)payload, len, ALP_MQTT_QOS_1, false);
210
+ printf("[mqtt] publish %u -> %s: %s\n", i, alp_status_name(s), payload);
211
+
212
+ /* Pace the publishes -- a real telemetry device sends on a
213
+ * timer, not in a tight loop. alp_delay_ms is portable
214
+ * (<alp/peripheral.h>, reached via <alp/iot.h>). */
215
+ if (i + 1u < PUBLISH_COUNT) {
216
+ alp_delay_ms(PUBLISH_PERIOD_S * 1000u);
217
+ }
218
+ }
219
+ }
220
+
221
+ /* native_sim / no-backend path: there's no live client, but print
222
+ * the payloads the app WOULD publish so the framing is observable
223
+ * in CI (and so a customer sees the message shape without a broker). */
224
+ static void telemetry_print_framing(void)
225
+ {
226
+ printf("[mqtt] no transport on this build -- printing framing only\n");
227
+ for (uint32_t i = 0; i < PUBLISH_COUNT; i++) {
228
+ char payload[96];
229
+ size_t len = build_payload(payload, sizeof(payload), read_telemetry_value(i));
230
+ if (len == 0u) {
231
+ continue;
232
+ }
233
+ printf("[mqtt] would publish to %s: %s\n", MQTT_TOPIC, payload);
234
+ }
235
+ }
236
+
237
+ int main(void)
238
+ {
239
+ /* Bring up the SDK runtime before anything else. */
240
+ (void)alp_init();
241
+
242
+ printf("[mqtt] alp-sdk mqtt-telemetry demo (publish over MQTT/TLS)\n");
243
+ printf("[mqtt] transport: CC3501E Wi-Fi bridge (E1M-AEN801)\n");
244
+
245
+ alp_wifi_t *w = telemetry_wifi_up();
246
+ if (w != NULL) {
247
+ alp_mqtt_t *m = telemetry_mqtt_up();
248
+ if (m != NULL) {
249
+ telemetry_publish_loop(m);
250
+ alp_mqtt_close(m);
251
+ }
252
+ alp_wifi_disconnect(w);
253
+ alp_wifi_close(w);
254
+ } else {
255
+ /* No Wi-Fi backend (native_sim): show the framing so the
256
+ * run is still meaningful + the harness has a marker. */
257
+ telemetry_print_framing();
258
+ }
259
+
260
+ printf("[mqtt] done\n");
261
+ return 0;
262
+ }