1.. _module-pw_system: 2 3========= 4pw_system 5========= 6.. pigweed-module:: 7 :name: pw_system 8 9.. warning:: 10 This module is an early work-in-progress towards an opinionated framework for 11 new projects built on Pigweed. It is under active development, so stay tuned! 12 13pw_system is quite different from typical Pigweed modules. Rather than providing 14a single slice of vertical functionality, pw_system pulls together many modules 15across Pigweed to construct a working system with RPC, Logging, an OS 16Abstraction layer, and more. pw_system exists to greatly simplify the process 17of starting a new project using Pigweed by drastically reducing the required 18configuration space required to go from first signs of on-device life to a more 19sophisticated production-ready system. 20 21-------------------- 22Trying out pw_system 23-------------------- 24If you'd like to give pw_system a spin and have a STM32F429I Discovery board, 25refer to the board's 26:ref:`target documentation<target-stm32f429i-disc1-stm32cube>` for instructions 27on how to build the demo and try things out 28 29If you don't have a discovery board, there's a simulated device variation that 30you can run on your local machine with no additional hardware. Check out the 31steps for trying this out :ref:`here<target-host-device-simulator>`. 32 33-------------- 34Target Bringup 35-------------- 36Bringing up a new device is as easy as 1-2-3! (Kidding, this is a work in 37progress) 38 39#. **Configure the build.** How exactly to do this depends on the build 40 system. 41 42 * **GN**: Create a ``pw_system_target`` in your GN build. This is what will 43 control the configuration of your target from a build system level. This 44 includes which compiler will be used, what architecture flags will be 45 used, which backends will be used, and more. A large quantity of 46 configuration will be pre-set to work with pw_system after you select the 47 CPU and scheduler your target will use, but your target will likely need 48 to set a few other things to get to a fully working state. 49 50 * **Bazel**: Add a dependency on ``@pigweed//pw_system`` to your ``cc_binary``, 51 and set one `label flag 52 <https://bazel.build/extending/config#label-typed-build-settings>`__, 53 ``@pigweed//pw_system:extra_platform_libs``. Point it to a ``cc_library`` 54 containing any platform-dependent dependencies of your ``pw_system`` 55 instantiation. In particular, this should include platform-specific 56 initialization code (see next point) and the custom 57 :ref:`pw_linker_script <module-pw_build-bazel-pw_linker_script>` (if any) 58 to use when linking the ``pw_system`` binary. 59 60 .. warning:: 61 62 You should always add the ``alwayslink = 1`` attribute to the target 63 you point ``@pigweed//pw_system:extra_platform_libs`` to. This is 64 because Bazel `links files in topological order 65 <https://stackoverflow.com/a/73006724/24291280>`__, but the 66 dependencies from ``extra_platform_libs`` may appear before the 67 objects they are used in. The ``alwayslink = 1`` will prevent the 68 linker from erroneously garbage-collecting them. 69 70#. **Write target-specific initialization.** 71 Most embedded devices require a linker script, manual initialization of 72 memory, and some clock initialization. pw_system leaves this to users to 73 implement as the exact initialization sequence can be very project-specific. 74 All that's required is that after early memory initialization and clock 75 configuration is complete, your target initialization should call 76 ``pw::system::Init()`` and then start the RTOS scheduler (e.g. 77 ``vTaskStartScheduler()``). 78#. **Implement ``pw::system::UserAppInit()`` in your application.** 79 This is where most of your project's application-specific logic goes. This 80 could be starting threads, registering RPC services, turning on Bluetooth, 81 or more. In ``UserAppInit()``, the RTOS will be running so you're free to use 82 OS primitives and use features that rely on threading (e.g. RPC, logging). 83 84Pigweed's ``stm32f429i_disc1_stm32cube`` target demonstrates what's required by 85the first two steps. The third step is where you get to decide how to turn your 86new platform into a project that does something cool! It might be as simple as 87a blinking LED, or something more complex like a Bluetooth device that brews you 88a cup of coffee whenever ``pw watch`` kicks off a new build. 89 90.. note:: 91 Because of the nature of the hard-coded conditions in ``pw_system_target``, 92 you may find that some options are missing for various RTOSes and 93 architectures. The design of the GN integration is still a work-in-progress 94 to improve the scalability of this, but in the meantime the Pigweed team 95 welcomes contributions to expand the breadth of RTOSes and architectures 96 supported as ``pw_system_target``\s. 97 98GN Target Toolchain Template 99============================ 100This module includes a target toolchain template called ``pw_system_target`` 101that reduces the amount of work required to declare a target toolchain with 102pre-selected backends for pw_log, pw_assert, pw_malloc, pw_thread, and more. 103The configurability and extensibility of this template is relatively limited, 104as this template serves as a "one-size-fits-all" starting point rather than 105being foundational infrastructure. 106 107.. code-block:: 108 109 # Declare a toolchain with suggested, compiler, compiler flags, and default 110 # backends. 111 pw_system_target("stm32f429i_disc1_stm32cube_size_optimized") { 112 # These options drive the logic for automatic configuration by this 113 # template. 114 cpu = PW_SYSTEM_CPU.CORTEX_M4F 115 scheduler = PW_SYSTEM_SCHEDULER.FREERTOS 116 117 # Optionally, override pw_system's defaults to build with clang. 118 system_toolchain = pw_toolchain_arm_clang 119 120 # The pre_init source set provides things like the interrupt vector table, 121 # pre-main init, and provision of FreeRTOS hooks. 122 link_deps = [ "$dir_pigweed/targets/stm32f429i_disc1_stm32cube:pre_init" ] 123 124 # These are hardware-specific options that set up this particular board. 125 # These are declared in ``declare_args()`` blocks throughout Pigweed. Any 126 # build arguments set by the user will be overridden by these settings. 127 build_args = { 128 pw_third_party_freertos_CONFIG = "$dir_pigweed/targets/stm32f429i_disc1_stm32cube:stm32f4xx_freertos_config" 129 pw_third_party_freertos_PORT = "$dir_pw_third_party/freertos:arm_cm4f" 130 pw_sys_io_BACKEND = dir_pw_sys_io_stm32cube 131 dir_pw_third_party_stm32cube = dir_pw_third_party_stm32cube_f4 132 pw_third_party_stm32cube_PRODUCT = "STM32F429xx" 133 pw_third_party_stm32cube_CONFIG = 134 "//targets/stm32f429i_disc1_stm32cube:stm32f4xx_hal_config" 135 pw_third_party_stm32cube_CORE_INIT = "" 136 pw_boot_cortex_m_LINK_CONFIG_DEFINES = [ 137 "PW_BOOT_FLASH_BEGIN=0x08000200", 138 "PW_BOOT_FLASH_SIZE=2048K", 139 "PW_BOOT_HEAP_SIZE=7K", 140 "PW_BOOT_MIN_STACK_SIZE=1K", 141 "PW_BOOT_RAM_BEGIN=0x20000000", 142 "PW_BOOT_RAM_SIZE=192K", 143 "PW_BOOT_VECTOR_TABLE_BEGIN=0x08000000", 144 "PW_BOOT_VECTOR_TABLE_SIZE=512", 145 ] 146 } 147 } 148 149 # Example for the Emcraft SmartFusion2 system-on-module 150 pw_system_target("emcraft_sf2_som_size_optimized") { 151 cpu = PW_SYSTEM_CPU.CORTEX_M3 152 scheduler = PW_SYSTEM_SCHEDULER.FREERTOS 153 154 link_deps = [ "$dir_pigweed/targets/emcraft_sf2_som:pre_init" ] 155 build_args = { 156 pw_log_BACKEND = dir_pw_log_basic #dir_pw_log_tokenized 157 pw_log_tokenized_HANDLER_BACKEND = "//pw_system:log" 158 pw_third_party_freertos_CONFIG = "$dir_pigweed/targets/emcraft_sf2_som:sf2_freertos_config" 159 pw_third_party_freertos_PORT = "$dir_pw_third_party/freertos:arm_cm3" 160 pw_sys_io_BACKEND = dir_pw_sys_io_emcraft_sf2 161 dir_pw_third_party_smartfusion_mss = dir_pw_third_party_smartfusion_mss_exported 162 pw_third_party_stm32cube_CONFIG = 163 "//targets/emcraft_sf2_som:sf2_mss_hal_config" 164 pw_third_party_stm32cube_CORE_INIT = "" 165 pw_boot_cortex_m_LINK_CONFIG_DEFINES = [ 166 "PW_BOOT_FLASH_BEGIN=0x00000200", 167 "PW_BOOT_FLASH_SIZE=200K", 168 169 # TODO: b/235348465 - Currently "pw_tokenizer/detokenize_test" requires at 170 # least 6K bytes in heap when using pw_malloc:bucket_block_allocator. 171 # The heap size required for tests should be investigated. 172 "PW_BOOT_HEAP_SIZE=7K", 173 "PW_BOOT_MIN_STACK_SIZE=1K", 174 "PW_BOOT_RAM_BEGIN=0x20000000", 175 "PW_BOOT_RAM_SIZE=64K", 176 "PW_BOOT_VECTOR_TABLE_BEGIN=0x00000000", 177 "PW_BOOT_VECTOR_TABLE_SIZE=512", 178 ] 179 } 180 } 181 182------- 183Metrics 184------- 185The log backend is tracking metrics to illustrate how to use pw_metric and 186retrieve them using `Device.get_and_log_metrics()`. 187 188------- 189Console 190------- 191The ``pw-system-console`` can be used to interact with the targets. 192See :ref:`module-pw_system-cli` for detailed CLI usage information. 193 194.. toctree:: 195 :hidden: 196 :maxdepth: 1 197 198 cli 199 200------------------- 201Multi-endpoint mode 202------------------- 203 204The default configuration serves all its traffic with the same 205channel ID and RPC address. There is an alternative mode that assigns a separate 206channel ID and address for logging. This can be useful if you want to separate logging and primary RPC to 207``pw_system`` among multiple clients. 208 209To use this mode, add the following to ``gn args out``: 210 211.. code-block:: 212 213 pw_system_USE_MULTI_ENDPOINT_CONFIG = true 214 215The settings for the channel ID and address can be found in the target 216``//pw_system:multi_endpoint_rpc_overrides``. 217 218.. _module-pw_system-logchannel: 219 220--------------------- 221Extra logging channel 222--------------------- 223In multi-processor devices, logs are typically forwarded to a primary 224application-class core. By default, ``pw_system`` assumes a simpler device 225architecture where a single processor is communicating with an external host 226system (e.g. a Linux workstation) for developer debugging. This means that 227logging and RPCs are expected to coexist on the same channel. It is possible 228to redirect the logs to a different RPC channel output by configuring 229``PW_SYSTEM_LOGGING_CHANNEL_ID`` to a different channel ID, but this would 230still mean that logs would inaccessible from either the application-class 231processor, or the host system. 232 233The logging multisink can be leveraged to address this completely by forwarding 234a copy of the logs to the application-class core without impacting the behavior 235of the debug communication channel. This allows ``pw-system-console`` work as 236usual, while also ensuring logs are available from the larger application-class 237processor. Additionally, this allows the debug channel to easily be disabled in 238production environments while maintaining the log forwarding path through the 239larger processor. 240 241An example configuration is provided below: 242 243.. code-block:: 244 245 config("extra_logging_channel") { 246 defines = [ "PW_SYSTEM_EXTRA_LOGGING_CHANNEL_ID=2" ] 247 } 248 249 pw_system_target("my_system") { 250 global_configs = [ ":extra_logging_channel" ] 251 } 252 253Once you have configured pw_system as shown in the example above, you will 254still need to define an RPC channel for the channel ID that you selected so 255the logs can be routed to the appropriate destination. 256 257--------------- 258pw_system:async 259--------------- 260``pw_system:async`` is a new version of ``pw_system`` based on 261:ref:`module-pw_async2` and :ref:`module-pw_channel`. It provides an async 262dispatcher, which may be used to run async tasks, including with C++20 263coroutines. 264 265To use ``pw_system:async``, add a dependency on ``@pigweed//pw_system:async`` in 266Bazel. Then, from your main function, invoke :cpp:func:`pw::SystemStart` with a 267:cpp:type:`pw::channel::ByteReaderWriter` to use for IO. 268 269.. literalinclude:: system_async_test.cc 270 :language: cpp 271 :linenos: 272 :start-after: [pw_system-async-example-main] 273 :end-before: [pw_system-async-example-main] 274 275pw_system:async Linux example 276============================= 277``//pw_system/system_async_host_simulator_example`` is an example app for 278running ``pw_system:async`` on a Linux host. Running the example requires two 279terminals. In the first terminal, start the ``pw_system:async`` instance: 280 281.. code-block:: console 282 283 $ bazelisk run //pw_system/system_async_host_simulator_example 284 285That will wait for a TCP connection from the ``pw_system`` console. To connect 286to it from the console, run the following: 287 288.. code-block:: console 289 290 $ bazelisk run //pw_system/py:pw_system_console -- -s 127.0.0.1:33000 291 292Debugging pw_system_console with VSCode 293--------------------------------------- 294When running a python script through bazel, python is run inside a bazel sandbox, 295which can make re-creating this environment difficult when running the script 296outside of bazel to attach a debugger. 297 298To simplify debugging setup, Pigweed makes available the `debugpy <https://github.com/microsoft/debugpy>`__ 299package to ease attaching ``pw_system_console``. 300 301First configure VSCode to add the following to the configuration section of ``launch.json``. 302This file can be automatically opened by selecting ``Run -> Open Configurations```, or 303``Run -> Add Configuration`` if there is no existing ``launch.json``. 304 305.. TODO: b/372079357 - can this be automated by the VSCode plugin? 306.. code-block:: json 307 308 "configurations": [ 309 { 310 "name": "Python Debugger: Remote Attach", 311 "type": "debugpy", 312 "request": "attach", 313 "connect": { 314 "host": "localhost", 315 "port": 5678 316 }, 317 "pathMappings": [ 318 { 319 "localRoot": "${workspaceFolder}", 320 "remoteRoot": "." 321 } 322 ] 323 } 324 325 ] 326 327Next, run the console through bazel, adding the argument(s) ``--debugger-listen`` and optionally 328``--debugger-wait-for-client`` to pause the console until the debugger attached. For example: 329 330.. code-block:: console 331 332 $ bazelisk run //pw_system/py:pw_system_console -- --debugger-listen 333 334Once the console has been started, simply select ``Run -> Start Debugging`` and the VS code debugger 335will automatically attach to the running python console. 336 337 338API reference 339============= 340.. doxygenfunction:: pw::SystemStart(channel::ByteReaderWriter&) 341.. doxygenfunction:: pw::System 342.. doxygenclass:: pw::system::AsyncCore 343 :members: 344