diff --git a/doc/hardware/arch/arm_cortex_m.rst b/doc/hardware/arch/arm_cortex_m.rst index 7e68398c9cc..7eef8e7f2f5 100644 --- a/doc/hardware/arch/arm_cortex_m.rst +++ b/doc/hardware/arch/arm_cortex_m.rst @@ -422,6 +422,8 @@ MPU stack guards detection mechanism; users may override this setting by manually enabling :kconfig:option:`CONFIG_MPU_STACK_GUARD` in these scenarios. +.. _arm_cortex_m_mpu_considerations: + Memory map and MPU considerations ================================= @@ -469,18 +471,19 @@ For example, to define a new non-cacheable memory region in devicetree: }; This will automatically create a new MPU entry in with the correct name, base, -size and attributes gathered directly from the devicetree. +size and attributes gathered directly from the devicetree. See :ref:`cache_guide` +and :ref:`mem_mgmt_api` for more details. Static MPU regions ------------------ Additional *static* MPU regions may be programmed once during system boot. These regions -are required to enable certain features +are required to enable certain features. See :ref:`cache_guide` for more details. * a RX region to allow execution from SRAM, when :kconfig:option:`CONFIG_ARCH_HAS_RAMFUNC_SUPPORT` is enabled and users have defined functions to execute from SRAM. * a RX region for relocating text sections to SRAM, when :kconfig:option:`CONFIG_CODE_DATA_RELOCATION_SRAM` is enabled -* a no-cache region to allow for a none-cacheable SRAM area, when :kconfig:option:`CONFIG_NOCACHE_MEMORY` is enabled +* a ``nocache`` region to allow for a non-cacheable SRAM area, when :kconfig:option:`CONFIG_NOCACHE_MEMORY` is enabled * a possibly unprivileged RW region for GCOV code coverage accounting area, when :kconfig:option:`CONFIG_COVERAGE_GCOV` is enabled * a no-access region to implement null pointer dereference detection, when :kconfig:option:`CONFIG_NULL_POINTER_EXCEPTION_DETECTION_MPU` is enabled diff --git a/doc/hardware/cache/guide.rst b/doc/hardware/cache/guide.rst new file mode 100644 index 00000000000..e2a8391895d --- /dev/null +++ b/doc/hardware/cache/guide.rst @@ -0,0 +1,183 @@ +.. _cache_guide: + +Caching Basics +############## + +This section discusses the basics of cache coherency and under what situations a +user needs to explicitly deal with caching. For more detailed info on Zephyr's +caching tools, see :ref:`cache_config` for Zephyr Kconfig options or +:ref:`cache_api` for the API reference. This section primarily focuses on the +data cache though there is typically also an instruction cache for systems with +cache support. + +.. note:: + + The information here assumes that the architecture-specific MPU support is + enabled. See the architecture-specific documentation for details. + +.. note:: + + While cache coherence can be a concern for data shared between SMP cores, Zephyr + in general ensures that memory will be seen in a coherent state from multiple + cores. Most applications will only need to use the cache APIs for interaction + with external hardware like DMA controllers or foreign CPUs running a + different OS image. For more information on cache coherence between SMP cores, + see :kconfig:option:`CONFIG_KERNEL_COHERENCE`. + +When dealing with memory shared between a processor core and other bus masters, +cache coherency needs to be considered. Typically processor caches exist as +close to each processor core as possible to maximize performance gain. Because +of this, data moved into and out of memory by DMA engines will be stale in the +processor's cache, resulting in what appears to be corrupt data. If you are +moving data using DMA and the processor doesn't see the data you expect, cache +coherency may be the issue. + +There are multiple approaches to ensuring that the data seen by the processor +core and peripherals is coherent. The simplest is just to disable caching, but +this defeats the purpose of having a hardware cache in the first place and +results in a significant performance hit. Many architectures provide methods for +disabling caching for only a portion of memory. This can be useful when cache +coherence is more important than performance, such as when using DMA with SPI. +Finally, there is the option to flush or invalidate the cache for regions of +memory at runtime. + +Globally Disabling the Data Cache +--------------------------------- + +As mentioned above, globally disabling data caching can have a significant +performance impact but can be useful for debugging. + +Requirements: + +* :kconfig:option:`CONFIG_DCACHE`: DCACHE control enabled in Zephyr. + +* :kconfig:option:`CONFIG_CACHE_MANAGEMENT`: cache API enabled. + +* Call :c:func:`sys_cache_data_disable()` to globally disable the data cache. + +Disabling Caching for a Memory Region +------------------------------------- + +Disabling caching for only a portion of memory can be a good performance +compromise if performance on the uncached memory is not critical to the +application. This is a good option if the application requires many small +unrelated buffers that are smaller than a cache line. + +Requirements: + +* :kconfig:option:`CONFIG_DCACHE`: DCACHE control enabled in Zephyr. + +* :kconfig:option:`CONFIG_MEM_ATTR`: enable the ``mem-attr`` library for + handling memory attributes in the device tree. + +* Annotate your device tree according to :ref:`mem_mgmt_api`. + +Assuming the MPU driver is enabled, it will configure the specified regions +according to the memory attributes specified during kernel initialization. When +using a dedicated uncached region of memory, the linker needs to be instructed +to place buffers into that region. This can be accomplished by specifying the +memory region explicitly using ``Z_GENERIC_SECTION``: + +.. code-block:: c + + /* SRAM4 marked as uncached in device tree */ + uint8_t buffer[BUF_SIZE] Z_GENERIC_SECTION("SRAM4"); + +.. note:: + + Configuring a distinct memory region with separate caching rules requires the + use of an MPU region which may be a limited resource on some architectures. + MPU regions may be needed by other memory protection features such as + :ref:`userspace `, :ref:`stack protection `, + or :ref:`memory domains`. + +Automatically Disabling Caching by Variable +------------------------------------------- + +Zephyr has the ability to automatically define an uncached region in memory and +allocate variables to it using ``__nocache``. Any variables marked with this +attribute will be placed in a special ``nocache`` linker region in memory. This +region will be configured as uncached by the MPU driver during initialization. +This is a simpler option than explicitly declaring a region of memory uncached +but provides less control over the placement of these variables, as the linker +may allocate this region anywhere in RAM. + +Requirements: + +* :kconfig:option:`CONFIG_DCACHE`: DCACHE control enabled in Zephyr. + +* :kconfig:option:`CONFIG_NOCACHE_MEMORY`: enable allocation of the ``nocache`` + linker region and configure it as uncached. + +* Add the ``__nocache`` attribute at the end of any uncached buffer definition: + +.. code-block:: c + + uint8_t buffer[BUF_SIZE] __nocache; + +.. note:: + + See note above regarding possible limitations on MPU regions. The ``nocache`` + region is still a distinct MPU region even though it is automatically created + by Zephyr instead of being explicitly defined by the user. + +Runtime Cache Control +--------------------- + +The most performant but most complex option is to control data caching at +runtime. The two most relevant cache operations in this case are **flushing** +and **invalidating**. Both of these operations operate on the smallest unit of +cacheable memory, the cache line. Data cache lines are typically 16 to 128 +bytes. See :kconfig:option:`CONFIG_DCACHE_LINE_SIZE`. Cache line sizes are +typically fixed in hardware and not configurable, but Zephyr does need to know +the size of cache lines in order to correctly and efficiently manage the cache. +If the buffers in question are smaller than the data cache line size, it may be +more efficient to place them in an uncached region, as unrelated data packed +into the same cache line may be destroyed when invalidating. + +Flushing the cache involves writing all modified cache lines in a specified +region back to shared memory. Flush the cache associated with a buffer after the +processor has written to it and before a remote bus master reads from that +region. + +.. note:: + + Some architectures support a cache configuration called **write-through** + caching in which data writes from the processor core propagate through to + shared memory. While this solves the cache coherence problem for CPU writes, + it also results in more traffic to main memory which may result in performance + degradation. + +Invalidating the cache works similarly but in the other direction. It marks +cache lines in the specified region as stale, ensuring that the cache line will +be refreshed from main memory when the processor next reads from the specified +region. Invalidate the data cache of a buffer that a peripheral has written to +before reading from that region. + +In some cases, the same buffer may be reused for e.g. DMA reads and DMA writes. +In that case it is possible to first flush the cache associated with a buffer +and then invalidate it, ensuring that the cache will be refreshed the next time +the processor reads from the buffer. + +Requirements: + +* :kconfig:option:`CONFIG_DCACHE`: DCACHE control enabled in Zephyr. + +* :kconfig:option:`CONFIG_CACHE_MANAGEMENT`: cache API enabled. + +* Call :c:func:`sys_cache_data_flush_range()` to flush a memory region. + +* Call :c:func:`sys_cache_data_invd_range()` to invalidate a memory region. + +* Call :c:func:`sys_cache_data_flush_and_invd_range()` to flush and invalidate. + +Alignment +--------- + +As mentioned in :c:func:`sys_cache_data_invd_range()` and associated functions, +buffers should be aligned to the cache line size. This can be accomplished by +using ``__aligned``: + +.. code-block:: c + + uint8_t buffer[BUF_SIZE] __aligned(CONFIG_DCACHE_LINE_SIZE); diff --git a/doc/hardware/cache/index.rst b/doc/hardware/cache/index.rst index 7f5fd5045e0..9e1ab1890ef 100644 --- a/doc/hardware/cache/index.rst +++ b/doc/hardware/cache/index.rst @@ -1,9 +1,9 @@ -.. _cache-guide: +.. _cache_config: -Cache Interface -############### +Cache Control Configuration +########################### -This is a high-level guide to cache interface and Kconfig options related to +This is a high-level guide to Zephyr's cache interface and Kconfig options related to cache controllers. See :ref:`cache_api` for API reference material. Zephyr has different Kconfig options to control how the cache controller is @@ -15,12 +15,14 @@ implemented and controlled. instruction cache. The cache controller can be in the core or can be an external cache controller for which a driver is provided. - These options have the goal to document an available feature and should be - set whether we plan to support and use the caches in Zephyr or not. + These options have the goal to document an available hardware feature and + should be set whether we plan to support and use the cache control in Zephyr + or not. * :kconfig:option:`CONFIG_DCACHE` / :kconfig:option:`CONFIG_ICACHE`: these options must be selected when support for data or instruction cache is - present and working in zephyr. + present and working in zephyr. Note that if these options are disabled, + caching may still be enabled depending on the hardware defaults. All the code paths related to cache control must be conditionally enabled depending on these symbols. When the symbol is set the cache is considered @@ -39,6 +41,15 @@ implemented and controlled. implemented in the architectural code or in an external cache controller driver. +* :kconfig:option:`CONFIG_MEM_ATTR`: this option allows the user to + specify (using :ref:`memory region attributes`) a fixed region + in memory that will have caching disabled once the kernel has initialized. + +* :kconfig:option:`CONFIG_NOCACHE_MEMORY`: this option allows the user to + specify individual global variables as uncached using ``__nocache``. This will + instruct the linker to place any marked variables into a special ``nocache`` + region in memory and the MPU driver will configure that region as uncached. + * :kconfig:option:`CONFIG_ARCH_CACHE`/:kconfig:option:`CONFIG_EXTERNAL_CACHE`: mutually exclusive options for :kconfig:option:`CACHE_TYPE` used to define whether the cache operations are implemented at arch level or using an @@ -54,6 +65,6 @@ implemented and controlled. .. _cache_api: Cache API -********* +######### .. doxygengroup:: cache_interface diff --git a/doc/hardware/index.rst b/doc/hardware/index.rst index 72e9cc5fd05..72697d09e72 100644 --- a/doc/hardware/index.rst +++ b/doc/hardware/index.rst @@ -8,6 +8,7 @@ Hardware Support arch/index.rst barriers/index.rst + cache/guide.rst cache/index.rst emulator/index.rst emulator/bus_emulators.rst diff --git a/doc/hardware/peripherals/dma.rst b/doc/hardware/peripherals/dma.rst index f2b998aaeda..575c42cde64 100644 --- a/doc/hardware/peripherals/dma.rst +++ b/doc/hardware/peripherals/dma.rst @@ -14,6 +14,10 @@ peripheral interactions, and features. The API in effect provides a union of all functionality drivers have needed in the tree. It can still be a good abstraction, with care, for peripheral devices for vendors where the DMA IP might be very similar but have slight variances. +The DMA drivers in general do not handle cache coherency; this is left up to the developer as +requirements vary dramatically depending on the application. See :ref:`cache_guide` for an +overview of cache management in Zephyr. + Driver Implementation Expectations ********************************** diff --git a/doc/kernel/usermode/memory_domain.rst b/doc/kernel/usermode/memory_domain.rst index f830cc981b9..9b9f31f64ed 100644 --- a/doc/kernel/usermode/memory_domain.rst +++ b/doc/kernel/usermode/memory_domain.rst @@ -28,7 +28,8 @@ contain the following: have an MPU region configuring it. It is strongly recommended to use this to maximize the number of available MPU regions for the end user. On ARMv7-M/ARMv8-M this is called the System Address Map, other CPUs may - have similar capabilities. + have similar capabilities. See :ref:`mem_mgmt_api` for information on + how to annotate the system map in the device tree. - A read-only, executable region or regions for program text and ro-data, that is accessible to user mode. This could be further sub-divided into a diff --git a/doc/services/mem_mgmt/index.rst b/doc/services/mem_mgmt/index.rst index 86d36b3961c..bcdef9fc3cb 100644 --- a/doc/services/mem_mgmt/index.rst +++ b/doc/services/mem_mgmt/index.rst @@ -45,7 +45,9 @@ regions out of devicetree defined memory regions, for example: }; See :zephyr_file:`include/zephyr/dt-bindings/memory-attr/memory-attr-arm.h` and -:ref:`arm_cortex_m_developer_guide` for more details about MPU usage. +:ref:`arm_cortex_m_mpu_considerations` in the :ref:`arm_cortex_m_developer_guide` +for more details about MPU usage. Also see :ref:`cache_guide` for details on how +Zephyr handles caching. The conventional and recommended way to deal and manage with memory regions marked with attributes is by using the provided ``mem-attr`` helper library by @@ -61,7 +63,10 @@ and act on regions and attributes (see next section for more details). actual setting for the memory to be set. The user, code or subsystem willing to use this information to do some work (for example creating an MPU region out of the property) must use either the provided ``mem-attr`` library or - the usual devicetree helpers to perform the required work / setting. + the usual devicetree helpers to perform the required work / setting. Note, + however, that for some architectures (such as ARM and ARM64) the MPU driver + uses this information to properly initialize caching at boot. See + :kconfig:option:`CONFIG_ARM_MPU`, :kconfig:option:`CONFIG_RISCV_PMP`, etc. A test for the ``mem-attr`` library and its usage is provided in ``tests/subsys/mem_mgmt/mem_attr/``.