xref: /aosp_15_r20/external/pigweed/pw_system/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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