1.. _module-pw_emu-guide: 2 3==================== 4Get started & guides 5==================== 6.. pigweed-module-subpage:: 7 :name: pw_emu 8 9.. _module-pw_emu-get-started: 10 11----------- 12Get started 13----------- 14.. tab-set:: 15 16 .. tab-item:: Bazel 17 18 ``pw_emu`` is currently only supported for GN-based projects. 19 20 .. tab-item:: GN 21 22 Include the desired emulator target files :ref:`pigweed.json 23 <seed-0101>`. For example: 24 25 .. code-block:: json 26 27 "pw_emu": { 28 "target_files": [ 29 "pw_emu/qemu-lm3s6965evb.json", 30 "pw_emu/qemu-stm32vldiscovery.json", 31 "pw_emu/qemu-netduinoplus2.json", 32 "renode-stm32f4_discovery.json" 33 ] 34 } 35 36 See :ref:`module-pw_emu-config-fragments` for examples of target files. 37 38 Build a target and then use ``pw emu run`` to run the target binaries on 39 the host. Continuing with the example: 40 41 .. code-block:: console 42 43 ninja -C out qemu_gcc 44 pw emu run --args=-no-reboot qemu-lm3s6965evb \ 45 out/lm3s6965evb_qemu_gcc_size_optimized/obj/pw_snapshot/test/cpp_compile_test 46 47 .. tab-item:: CMake 48 49 ``pw_emu`` is not currently supported for CMake-based projects. 50 51 .. tab-item:: Zephyr 52 53 ``pw_emu`` is not currently supported for Zephyr-based projects. 54 55 .. tab-item:: Android host 56 57 Create a :ref:`pigweed.json <seed-0101>` file and include the 58 desired emulator targets. 59 60 Build the ``pw emu`` standalone Android Python executable: 61 62 .. code-block:: console 63 64 m pw_emu_py 65 66 Run ``pw emu``: 67 68 .. code-block:: console 69 70 pw_emu_py -c <path to pigweed.json> start <emulator target> 71 72------------------------ 73Set up emulation targets 74------------------------ 75An emulator target can be defined directly in the ``pw.pw_emu`` namespace of 76:ref:`pigweed.json <seed-0101>`, like this: 77 78.. code-block:: 79 80 { 81 "pw": { 82 "pw_emu": { 83 "targets": { 84 "qemu-lm3s6965evb": { 85 "...": "..." 86 } 87 } 88 } 89 } 90 } 91 92Or it can be defined elsewhere and then imported into ``pigweed.json``, like 93this: 94 95.. code-block:: 96 97 { 98 "pw": { 99 "pw_emu": { 100 "target_files": [ 101 "qemu-lm3s6965evb.json" 102 ] 103 } 104 } 105 } 106 107Relative paths are interpreted relative to the root of your project directory. 108 109You can configure default options at the emulator level and then override 110those options at the target or channel level. See :ref:`module-pw_emu-config` 111for details. 112 113QEMU targets 114============ 115When defining a QEMU emulation target the following keys must be defined 116under ``<target>.qemu`` (where ``<target>`` is a placeholder for a real target 117name): 118 119* ``executable`` - The name of the QEMU executable, e.g. ``qemu-system-arm``, 120 ``qemusystem-riscv64``, etc. 121* ``machine`` - The QEMU machine name. See ``qemu-system-<arch> -machine help`` 122 for a list of supported machines names. 123 124The following example is a :ref:`config fragment <module-pw_emu-config-fragments>` 125for a target that runs on QEMU: 126 127.. literalinclude:: qemu-lm3s6965evb.json 128 129.. note:: 130 131 Since this is an Arm machine the QEMU executable is defined as 132 ``qemu-system-arm``. 133 134QEMU chardevs can be exposed as host channels under 135``<target>.qemu.channels.chardevs.<chan-name>`` where ``<chan-name>`` is 136the name that the channel can be accessed with (e.g. ``pw emu term 137<chan-name>``). The ``id`` option is mandatory; it should match a valid 138chardev ``id`` for this particular QEMU machine. 139 140The example target above emulates a `Stellaris EEVB 141<https://www.ti.com/product/LM3S6965>`_ and is compatible with the 142:ref:`target-lm3s6965evb-qemu` Pigweed target. The configuration defines a 143``serial0`` channel to be the QEMU ``chardev`` with an ``id`` of ``serial0``. 144The default channel type (``tcp``) is used, which is supported by all platforms. 145You can change the type by adding a ``type`` key set to the desired type, e.g. 146``pty``. 147 148Renode targets 149============== 150The following example is a :ref:`config fragment <module-pw_emu-config-fragments>` 151for a target that runs on Renode: 152 153.. literalinclude:: renode-stm32f4_discovery.json 154 155This target emulates the `ST 32F429I Discovery Kit 156<https://www.st.com/en/evaluation-tools/32f429idiscovery.html>`_ and is 157compatible with the :ref:`target-stm32f429i-disc1` Pigweed target. ``machine`` 158identifies which Renode script to use for the machine definitions. ``terminals`` 159defines which UART devices to expose to the host. ``serial0`` exposes the serial 160port identified as ``sysbus.usart1`` in the Renode machine script. 161 162------------------- 163Run target binaries 164------------------- 165Use ``pw emu run`` to quickly run target binaries on the host. ``pw emu run`` 166starts an emulator instance, connects to a given serial port, and then loads 167and runs the given binary. 168 169.. code-block:: console 170 171 $ pw emu run --args=-no-reboot qemu-lm3s6965evb \ 172 out/lm3s6965evb_qemu_gcc_size_optimized/obj/pw_snapshot/test/cpp_compile_test 173 174 --- Miniterm on serial0 --- 175 --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- 176 INF [==========] Running all tests. 177 INF [ RUN ] Status.CompileTest 178 INF [ OK ] Status.CompileTest 179 INF [==========] Done running all tests. 180 INF [ PASSED ] 1 test(s). 181 --- exit --- 182 183.. note:: 184 185 The ``-no-reboot`` option is passed directly to QEMU and instructs the 186 emulator to quit instead of rebooting. 187 188--------- 189Debugging 190--------- 191Use ``pw emu gdb`` to debug target binaries. 192 193.. note:: 194 195 You always need to run an emulator instance (``pw emu start``) before 196 starting a debug session. 197 198In the following example, ``status_test`` from ``pw_status`` is debugged. The 199binary was compiled for :ref:`target-lm3s6965evb-qemu`. First an emulator 200instance is started using the ``qemu-lm3s6965evb`` emulation target definition 201and then the test file is loaded: 202 203.. code-block:: console 204 205 $ pw emu start qemu-lm3s6965evb \ 206 --file out/lm3s6965evb_qemu_gcc_size_optimized/obj/pw_status/test/status_test 207 208Next, a ``gdb`` session is started and connected to the emulator instance: 209 210.. code-block:: console 211 212 $ pw emu gdb -e out/lm3s6965evb_qemu_gcc_size_optimized/obj/pw_status/test/status_test 213 GNU gdb (Arm GNU Toolchain 12.2.MPACBTI-Rel1 ... 214 ... 215 Reading symbols from out/stm32f429i_disc1_debug/obj/pw_status/test/status_test.elf... 216 Remote debugging using ::1:32979 217 pw::sys_io::WriteByte (b=(unknown: 0x20)) at pw_sys_io_baremetal_lm3s6965evb/sys_io_baremetal.cc:117 218 117 uart0.data_register = static_cast<uint32_t>(b); 219 (gdb) bt 220 #0 pw::sys_io::WriteByte (b=(unknown: 0x20)) at pw_sys_io_baremetal_lm3s6965evb/sys_io_baremetal.cc:117 221 #1 0x00002f6a in pw::sys_io::WriteBytes (src=...) at pw_span/public/pw_span/internal/span_impl.h:408 222 #2 0x00002eca in pw::sys_io::WriteLine (s=...) at pw_span/public/pw_span/internal/span_impl.h:264 223 #3 0x00002f92 in operator() (log=..., __closure=0x0 <vector_table>) at pw_log_basic/log_basic.cc:87 224 #4 _FUN () at pw_log_basic/log_basic.cc:89 225 #5 0x00002fec in pw::log_basic::pw_Log (level=<optimized out>, flags=<optimized out>, module_name=<optimized out>, file_name=<optimized out>, line_number=95, 226 function_name=0x6e68 "TestCaseStart", message=0x6e55 "[ RUN ] %s.%s") at pw_log_basic/log_basic.cc:155 227 #6 0x00002b0a in pw::unit_test::LoggingEventHandler::TestCaseStart (this=<optimized out>, test_case=...) at pw_unit_test/logging_event_handler.cc:95 228 #7 0x00000f54 in pw::unit_test::internal::Framework::CreateAndRunTest<pw::(anonymous namespace)::Status_Strings_Test> (test_info=...) 229 at pw_unit_test/public/pw_unit_test/internal/framework.h:266 230 #8 0x0000254a in pw::unit_test::internal::TestInfo::run (this=0x20000280 <_pw_unit_test_Info_Status_Strings>) 231 at pw_unit_test/public/pw_unit_test/internal/framework.h:413 232 #9 pw::unit_test::internal::Framework::RunAllTests (this=0x20000350 <pw::unit_test::internal::Framework::framework_>) at pw_unit_test/framework.cc:64 233 #10 0x000022b0 in main (argc=<optimized out>, argv=<optimized out>) at pw_unit_test/public/pw_unit_test/internal/framework.h:218 234 235At this point you can debug the program with ``gdb`` commands. 236 237To stop the debugging session: 238 239.. code-block:: console 240 241 $ pw emu stop 242 243-------------- 244Boot debugging 245-------------- 246Use the ``-p`` or ``--pause`` option when starting an emulator to debug 247bootstrapping code: 248 249.. code-block:: console 250 251 $ pw emu start -p qemu-lm3s6965evb \ 252 --file out/lm3s6965evb_qemu_gcc_size_optimized/obj/pw_status/test/status_test 253 254The given program loads but the emulator doesn't start executing. Next, start 255a debugging session with ``pw emu gdb``: 256 257.. code-block:: console 258 259 $ pw emu gdb -e out/lm3s6965evb_qemu_gcc_size_optimized/obj/pw_status/test/status_test 260 GNU gdb (Arm GNU Toolchain 12.2.MPACBTI-Rel1 ... 261 ... 262 Reading symbols from out/lm3s6965evb_qemu_gcc_size_optimized//obj/pw_status/test/status_test... 263 Remote debugging using ::1:38723 264 pw_boot_Entry () at pw_boot_cortex_m/core_init.c:122 265 122 asm volatile("cpsid i"); 266 (gdb) 267 268The program stops at the :ref:`pw_boot_Entry() <module-pw_boot>` function. From 269here you can add breakpoints or step through the program with ``gdb`` commands. 270 271------------------------------- 272Run multiple emulator instances 273------------------------------- 274Use the ``-i`` or ``--instance`` option to run multiple emulator instances. 275 276.. note:: 277 278 Internally each emulator instance is identified by a working directory. The 279 working directory for ``pw_emu`` is ``$PROJECT_ROOT/.pw_emu/<instance-id>``. 280 281The next example attempts to start two emulators at the same time: 282 283.. code-block:: console 284 285 $ pw emu start qemu-lm3s6965evb --file out/lm3s6965evb_qemu_gcc_size_optimized/obj/pw_status/test/status_test 286 $ pw emu start qemu-lm3s6965evb --file out/lm3s6965evb_qemu_gcc_size_optimized/obj/pw_status/test/status_test 287 pigweed/.pw_emu/default: emulator already started 288 289 290This fails because ``pw emu`` attempts to assign the same default instance 291ID to each instance. The default ID is ``default``. To fix this, assign the 292second instance a custom ID: 293 294.. code-block:: console 295 296 $ pw emu start qemu-lm3s6965evb --file out/lm3s6965evb_qemu_gcc_size_optimized/obj/pw_status/test/status_test 297 $ pw emu -i instance2 start qemu-lm3s6965evb --file out/lm3s6965evb_qemu_gcc_size_optimized/obj/pw_status/test/status_test 298 299To stop both emulator instances: 300 301.. code-block:: console 302 303 $ pw emu stop 304 $ pw emu stop -i instance2 305 306------------------------- 307Adding new emulator types 308------------------------- 309``pw_emu`` can be extended to support new emulator types by providing 310implementations for :py:class:`pw_emu.core.Launcher` and 311:py:class:`pw_emu.core.Connector` in a dedicated ``pw_emu`` Python module 312(e.g. :py:mod:`pw_emu.myemu`) or in an external Python module. 313 314Internal ``pw_emu`` modules must register the connector and launcher 315classes by updating :py:obj:`pw_emu.pigweed_emulators.pigweed_emulators`. For 316example, the QEMU implementation sets the following values: 317 318.. code-block:: py 319 320 pigweed_emulators: dict[str, dict[str, str]] = { 321 ... 322 'qemu': { 323 'connector': 'pw_emu.qemu.QemuConnector', 324 'launcher': 'pw_emu.qemu.QemuLauncher', 325 }, 326 ... 327 } 328 329For external emulator frontend modules, ``pw_emu`` uses 330:ref:`pigweed.json <seed-0101>` to determine the connector and launcher 331classes under ``pw_emu.emulators.<emulator-name>.connector`` and 332``pw_emu.emulators:<emulator-name>.launcher``. 333 334Configuration example: 335 336.. code-block:: 337 338 { 339 "pw": { 340 "pw_emu": { 341 "emulators": [ 342 "myemu": { 343 "launcher": "mypkg.mymod.mylauncher", 344 "connector": "mypkg.mymod.myconnector", 345 } 346 ] 347 } 348 } 349 } 350 351The :py:class:`pw_emu.core.Launcher` implementation must implement the following 352methods: 353 354* :py:meth:`pw_emu.core.Launcher._pre_start` 355* :py:class:`pw_emu.core.Launcher._post_start` 356* :py:class:`pw_emu.core.Launcher._get_connector` 357 358There are several abstract methods that need to be implemented for the 359connector, like :py:meth:`pw_emu.core.Connector.reset` and 360:py:meth:`pw_emu.core.Connector.cont`. These are typically implemented using 361internal channels and :py:class:`pw_emu.core.Connector.get_channel_stream`. See 362:py:class:`pw_emu.core.Connector` for a complete list of the abstract methods 363that need to be implemented. 364 365---------------- 366More pw_emu docs 367---------------- 368.. include:: docs.rst 369 :start-after: .. pw_emu-nav-start 370 :end-before: .. pw_emu-nav-end 371