• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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