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