From 6cc346fdb754fe715dc7b461db74103332d149be Mon Sep 17 00:00:00 2001 From: Alberto Escolar Piedras Date: Tue, 24 Sep 2024 17:18:30 +0200 Subject: [PATCH] boards/native/doc: Update architecture description The architecture description was quite out of date in some parts, specially those which changed with the introduction of the native simulator. Let's update it. Signed-off-by: Alberto Escolar Piedras --- boards/native/doc/arch_soc.rst | 245 ++++++++++++++---------- boards/native/doc/components_natsim.svg | 4 + 2 files changed, 150 insertions(+), 99 deletions(-) create mode 100644 boards/native/doc/components_natsim.svg diff --git a/boards/native/doc/arch_soc.rst b/boards/native/doc/arch_soc.rst index d07a9c589a2c9a..8906001f17763d 100644 --- a/boards/native/doc/arch_soc.rst +++ b/boards/native/doc/arch_soc.rst @@ -11,8 +11,8 @@ The POSIX architecture Overview ******** -The POSIX architecture, in combination with the inf_clock SOC layer, -provides the foundation, architecture and SOC layers for a set of virtual test +The native simulator in combination with the POSIX architecture and the inf_clock SOC layer, +provide the foundation, architecture and SOC layers for a set of virtual test boards. Using these, a Zephyr application can be compiled together with @@ -63,8 +63,8 @@ This port is designed and tested to run in Linux. .. note:: - The 32 bit version of this port does not directly work in Windows Subsystem - for Linux (WSL) because WSL does not support native 32-bit binaries. + The 32 bit version of this port does not directly work in the old Windows Subsystem + for Linux (WSL1) because WSL1 does not support native 32-bit binaries. You may want to consider WSL2, or, if using :ref:`native_sim `, you can also just use the ``native_sim/native/64`` target: Check :ref:`32 and 64bit versions`. @@ -304,128 +304,171 @@ side-effects. Architecture and design *********************** -.. figure:: layering.svg +.. note:: + + This section does not describe anymore the old :ref:`native_posix` or + :kconfig:option:`CONFIG_NATIVE_APPLICATION` based architecture. + It only describes the new native simulator based architecture used by targets built with the + :kconfig:option:`CONFIG_NATIVE_LIBRARY` option. + +.. note:: + + This description applies to the boards on the tree, + but it is not a requirement for other POSIX arch based boards to follow what is described here. + +.. figure:: layering_natsim.svg + :align: center + :alt: Zephyr layering in a native simulator build + :figclass: align-center + + Zephyr layering when built against an embedded target (left), and targeting a native_simulator + based board (right) + +.. figure:: components_natsim.svg :align: center - :alt: Zephyr layering in native build + :alt: native_sim boards and the native simulator :figclass: align-center - Zephyr layering when built against an embedded target (left), and - targeting a POSIX arch based board (right) + Relationship between Zephyr, the native_sim target and the native simulator + +When building targeting Zephyr's :ref:`native_sim` board, we build our embedded SW, +that is, our application, the Zephyr kernel, and any subsystems and drivers we have selected, +with the :ref:`POSIX architecture` and the +:ref:`inf_clock` SOC layers. +The result of this build is a pre-linked elf library, which contains what we can call the +embedded SW. +Then the `native simulator `_ runner will be built. +And after both the "embedded SW" and the runner will be linked together to form the final Linux +executable. +This final executable is typically called ``zephyr.exe`` and can be run or debugged just like any +other normal Linux executable. + +The native simulator runner provides the Linux program entry point, command line argument parsing, +the HW models scheduler, as well as a component to emulate the CPU start/stop and CPU thread +switching. +It also provides a mechanism to register functions which need to be run at different points of the +executable lifetime. +When targeting native_sim, the native simulator is also built with some basic HW models like a +system timer and an interrupt controller. +You can find more information on these in the +`native simulator design documentation `_. + +The native_sim target is a single microcontroller (MCU) target with simple HW models. Other targets +like the :ref:`simulated nRF5340 (nrf5340bsim)` are multi MCU targets. Where one +embedded Zephyr image can be build for each MCU, and all MCU images and the runner are assembled +together into one executable with more elaborate HW models of those SOCs. + +Native simulator runner context and the embedded context +======================================================== + +It is worth noting that the embedded SW library is first pre-linked. That is that all symbols which +can be resolved inside that library will be resolved. And that, after, all these library symbols, +except a selected few marked with an special annotation, will be hidden from further linking. +In this way, the runner link stage will not link to or conflict with any of these hidden symbols, +neither from the runner itself or from other CPUs embedded SW libraries. +It is also worth noting that all expected Zephyr sections are built and ordered with the Zephyr +linker script in that first embedded SW library link. + +When the embedded SW is built, one has the option of linking an embedded C standard library with it, +or leave at that point all C library calls unresolved, and let them be linked in the final stage +with the host C library. + +Due to all this, we can conceptually see our build divided in two separate contexts: +One is the embedded/Zephyr context, in which we build the Zephyr OS, an application for a given MCU, +and which may be built with an embedded C library. +Another is the runner context, which is always built with the host C library and which has very +limited visibility into the embedded context. + +From the embedded context we can easily call into the runner context: All runner context symbols +will be linkable in the final link stage unless another embedded symbol with the same name was +already linked to it in the first pass. +But from the runner context only the symbols from the embedded context annotated with the +``NATIVE_SIMULATOR_IF`` macro will be linkable. + +From Zephyr's build system it is possible to request a file to be built in the runner context by +adding it to the cmake ``native_simulator`` library target. You can check +:zephyr_file:`arch/posix/CMakeLists.txt` for more information. + +You can find more information in the native simulator +`build documentation `_ .. _posix_arch_design_archl: Arch layer ========== -In this architecture each Zephyr thread is mapped to one POSIX pthread. -The POSIX architecture emulates a single threaded CPU/MCU by only allowing -one SW thread to execute at a time, as commanded by the Zephyr kernel. -Whenever the Zephyr kernel desires to context switch two threads, -the POSIX arch blocks and unblocks the corresponding pthreads. +The POSIX architecture is mainly responsible for two things: + +* Set up the Zephyr build to produce an static library for the host architecture to be later + used with the native simulator build and linked with the native simulator runner. +* Provide a thin adaptation between the API the Zephyr kernel expects from an architecture + layer and the native simulator CPU threading emulation (NCT). + +This layer together with the NCT maps each Zephyr thread into one POSIX pthread, and emulates a +single threaded CPU/MCU by only allowing one SW thread to execute at a time, as commanded by the +Zephyr kernel. Whenever the Zephyr kernel desires to context switch two threads, the POSIX arch, +using NCT, blocks and unblocks the corresponding pthreads. This architecture provides the same interface to the Kernel as other architectures and is therefore transparent for the application. +Note that all threads use a normal Linux pthread stack, and do not use +the Zephyr thread stack allocation for their call stacks or automatic +variables. The Zephyr stacks (which are allocated in "static memory") are +only used by the POSIX architecture to keep thread bookkeeping data. + When using this architecture, the code is compiled natively for the host system, and typically as a 32-bit binary assuming pointer and integer types are 32-bits wide. -Note that all threads use a normal Linux pthread stack, and do not use -the Zephyr thread stack allocation for their call stacks or automatic -variables. The Zephyr stacks (which are allocated in "static memory") are -only used by the POSIX architecture for thread bookkeeping. +.. _posix_arch_design_socl: -SOC and board layers -==================== +SOC layer +========= -.. note:: +This SOC layer is mainly a very thin layer on top of the native simulator CPU emulation layer, +which is responsible for handling the simulation of the CPU start/stop, as well as the +initialization of the arch layer, and calling into the Zephyr boot (:c:func:`z_cstart()`) during +the CPU boot itself. + +It also provides the :ref:`native_tasks`, and specifies +a few other hooks it expects the board layer to provide. + +Board layer +=========== + +The board layer is responsible to provide all the hooks the SOC layer and native simulator runner +expect. This includes the hooks to boot the CPU (which call into the SOC layer), to handle +interrupts, and the hooks for low level tracing and busy wait handling. + +The overall execution and scheduling is handled by the native simulator runner itself, which calls +when necessary into the board layer hooks. +You can find information about how the native simulator runs the embedded SW in its +`design documentation `_ + +For more complex simulated boards, like :ref:`bsim ones`, the board layer also provides +the necessary logic and configuration to mimic a real target and SOC. + +Note that the SOC/board split in this architecture is different than for other Zephyr targets. +This was done to enable very different real SOC simulations to share a common architecture and SOC +layer, while placing the real SOC specific replacement logic in the board layer. - This description applies to all current POSIX arch based boards on tree, - but it is not a requirement for another board to follow what is described here. - -When the executable process is started (that is the board -:c:func:`main`, which is the linux executable C :c:func:`main`), -first, early initialization steps are taken care of -(command line argument parsing, initialization of the HW models, etc). - -After, the "CPU simulation" is started, by creating a new pthread -and provisionally blocking the original thread. The original thread will only -be used for HW models after this; -while this newly created thread will be the first "SW" thread and start -executing the boot of the embedded code (including the POSIX arch code). - -During this MCU boot process, the Zephyr kernel will be initialized and -eventually this will call into the embedded application ``main()``, -just like in the embedded target. -As the embedded SW execution progresses, more Zephyr threads may be spawned, -and for each the POSIX architecture will create a dedicated pthread. - -Eventually the simulated CPU will be put to sleep by the embedded SW -(normally when the boot is completed). This whole simulated CPU boot, -until the first time it goes to sleep happens in 0 simulated time. - -At this point the last executing SW pthread will be blocked, -and the first thread (reserved for the HW models now) will be allowed -to execute again. This thread will, from now on, be the one handling both the -HW models and the device simulated time. - -The HW models are designed around timed events, -and this thread will check what is the next -scheduled HW event, advance simulated time until that point, and call the -corresponding HW model event function. - -Eventually one of these HW models will raise an interrupt to the -simulated CPU. When the IRQ controller wants to wake the simulated -CPU, the HW thread is blocked, and the simulated CPU is awakened by -letting the last SW thread continue executing. - -This process of getting the CPU to sleep, letting the HW models run, -and raising an interrupt which wake the CPU again is repeated until the end -of the simulation, where the CPU execution always takes 0 simulated time. - -When a SW thread is awakened by an interrupt, it will be made to enter the -interrupt handler by the soc_inf code. - -If the SW unmasks a pending interrupt while running, or triggers a SW -interrupt, the interrupt controller may raise the interrupt immediately -depending on interrupt priorities, masking, and locking state. - -Interrupts are executed in the context (and using the stack) of the SW -thread in which they are received. Meaning, there is no dedicated thread or -stack for interrupt handling. - -To ensure determinism when the Zephyr code is running, -and to ease application debugging, -the board uses a different time than real time: simulated time. -How and if simulated time relates to the host time, is up to the simulated -board. - -The Zephyr application sees the code executing as if the CPU were running at -an infinitely fast clock, and fully decoupled from the underlying host CPU -speed. -No simulated time passes while the application or kernel code execute. .. _posix_busy_wait: Busy waits ========== -Busy waits work thanks to provided board functionality. -This does not need to be the same for all boards, but both native_sim and the -nrf52_bsim board work similarly thru the combination of a board specific -:c:func:`arch_busy_wait()` and a special fake HW timer (provided by the board). +Busy waits work thanks to logic provided by the board and native simulator. +This does not need to be the same for all boards, but both :ref:`native_sim` and the +:ref:`nrf*bsim boards` work similarly through the combination of a board specific +:c:func:`arch_busy_wait()` and an special fake HW timer provided by the native simulator. -When a SW thread wants to busy wait, this fake timer will be programmed in -the future time corresponding to the end of the busy wait and the CPU will -be put immediately to sleep in the busy_wait caller context. -When this fake HW timer expires the CPU will be waken with a special -non-maskable phony interrupt which does not have a corresponding interrupt -handler but will resume the busy_wait SW execution. -Note that other interrupts may arrive while the busy wait is in progress, -which may delay the :c:func:`k_busy_wait()` return just like in real life. +Please check the +`native simulator busy wait design documentation `_ +for more info. -Interrupts may be locked out or masked during this time, but the special -fake-timer non-maskable interrupt will wake the CPU nonetheless. +.. _posix_arch_design_native_tasks: NATIVE_TASKS @@ -433,9 +476,13 @@ NATIVE_TASKS The soc_inf layer provides a special type of hook called the NATIVE_TASKS. -These allow registering (at build/link time) functions which will be called +These allow registering (at build/link time) embedded context functions which will be called at different stages during the process execution: Before command line parsing (so dynamic command line arguments can be registered using this hook), before initialization of the HW models, before the simulated CPU is started, after the simulated CPU goes to sleep for the first time, and when the application exists. + +These hooks are ultimately based on the +`native simulator tasks `_ +which the users may also register from code built in the runner context. diff --git a/boards/native/doc/components_natsim.svg b/boards/native/doc/components_natsim.svg new file mode 100644 index 00000000000000..01723d32ed4320 --- /dev/null +++ b/boards/native/doc/components_natsim.svg @@ -0,0 +1,4 @@ + + + +
Zephyr's native_sim target
Zephyr's native_sim...
native simulator
native simulator
Uses
Uses
Embedded SW:
Application + Zephyr kernel + subsystems + drivers
Embedded SW:...
Built for
Built for
\ No newline at end of file