.. _mem_mgmt_api:

Memory Attributes
#################

It is possible in the devicetree to mark the memory regions with attributes by
using the ``zephyr,memory-attr`` property. This property and the related memory
region can then be retrieved at run-time by leveraging a provided helper
library.

The set of general attributes that can be specified in the property are defined
and explained in :zephyr_file:`include/zephyr/dt-bindings/memory-attr/memory-attr.h`.

For example, to mark a memory region in the devicetree as non-volatile, cacheable,
out-of-order:

.. code-block:: devicetree

   mem: memory@10000000 {
       compatible = "mmio-sram";
       reg = <0x10000000 0x1000>;
       zephyr,memory-attr = <( DT_MEM_NON_VOLATILE | DT_MEM_CACHEABLE | DT_MEM_OOO )>;
   };

.. note::

   The ``zephyr,memory-attr`` usage does not result in any memory region
   actually created. When it is needed to create an actual section out of the
   devicetree defined memory region, it is possible to use the compatible
   :dtcompatible:`zephyr,memory-region` that will result (only when supported
   by the architecture) in a new linker section and region.

The ``zephyr,memory-attr`` property can also be used to set
architecture-specific and software-specific custom attributes that can be
interpreted at run time. This is leveraged, among other things, to create MPU
regions out of devicetree defined memory regions, for example:

.. code-block:: devicetree

   mem: memory@10000000 {
       compatible = "mmio-sram";
       reg = <0x10000000 0x1000>;
       zephyr,memory-region = "NOCACHE_REGION";
       zephyr,memory-attr = <( DT_MEM_ARM(ATTR_MPU_RAM_NOCACHE) )>;
   };

See :zephyr_file:`include/zephyr/dt-bindings/memory-attr/memory-attr-arm.h` and
: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
enabling :kconfig:option:`CONFIG_MEM_ATTR`. When this option is enabled the
list of memory regions and their attributes are compiled in a user-accessible
array and a set of functions is made available that can be used to query, probe
and act on regions and attributes (see next section for more details).

.. note::

   The ``zephyr,memory-attr`` property is only a descriptive property of the
   capabilities of the associated memory region, but it does not result in any
   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. 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/``.

Migration guide from ``zephyr,memory-region-mpu``
*************************************************

When the ``zephyr,memory-attr`` property was introduced, the
``zephyr,memory-region-mpu`` property was removed and deprecated.

The developers that are still using the deprecated property can move to the new
one by renaming the property and changing its value according to the following list:

.. code-block:: none

   "RAM"         -> <( DT_ARM_MPU(ATTR_MPU_RAM) )>
   "RAM_NOCACHE" -> <( DT_ARM_MPU(ATTR_MPU_RAM_NOCACHE) )>
   "FLASH"       -> <( DT_ARM_MPU(ATTR_MPU_FLASH) )>
   "PPB"         -> <( DT_ARM_MPU(ATTR_MPU_PPB) )>
   "IO"          -> <( DT_ARM_MPU(ATTR_MPU_IO) )>
   "EXTMEM"      -> <( DT_ARM_MPU(ATTR_MPU_EXTMEM) )>

Memory Attributes Heap Allocator
********************************

It is possible to leverage the memory attribute property ``zephyr,memory-attr``
to define and create a set of memory heaps from which the user can allocate
memory from with certain attributes / capabilities.

When the :kconfig:option:`CONFIG_MEM_ATTR_HEAP` is set, every region marked
with one of the memory attributes listed in
:zephyr_file:`include/zephyr/dt-bindings/memory-attr/memory-attr-sw.h` is added
to a pool of memory heaps used for dynamic allocation of memory buffers with
certain attributes.

Here a non exhaustive list of possible attributes:

.. code-block:: none

   DT_MEM_SW_ALLOC_CACHE
   DT_MEM_SW_ALLOC_NON_CACHE
   DT_MEM_SW_ALLOC_DMA

For example we can define several memory regions with different attributes and
use the appropriate attribute to indicate that it is possible to dynamically
allocate memory from those regions:

.. code-block:: devicetree

   mem_cacheable: memory@10000000 {
       compatible = "mmio-sram";
       reg = <0x10000000 0x1000>;
       zephyr,memory-attr = <( DT_MEM_CACHEABLE | DT_MEM_SW_ALLOC_CACHE )>;
   };

   mem_non_cacheable: memory@20000000 {
       compatible = "mmio-sram";
       reg = <0x20000000 0x1000>;
       zephyr,memory-attr = <( DT_MEM_NON_CACHEABLE | ATTR_SW_ALLOC_NON_CACHE )>;
   };

   mem_cacheable_big: memory@30000000 {
       compatible = "mmio-sram";
       reg = <0x30000000 0x10000>;
       zephyr,memory-attr = <( DT_MEM_CACHEABLE | DT_MEM_OOO | DT_MEM_SW_ALLOC_CACHE )>;
   };

   mem_cacheable_dma: memory@40000000 {
       compatible = "mmio-sram";
       reg = <0x40000000 0x10000>;
       zephyr,memory-attr = <( DT_MEM_CACHEABLE      | DT_MEM_DMA |
                               DT_MEM_SW_ALLOC_CACHE | DT_MEM_SW_ALLOC_DMA )>;
   };

The user can then dynamically carve memory out of those regions using the
provided functions, the library will take care of allocating memory from the
correct heap depending on the provided attribute and size:

.. code-block:: c

   // Init the pool
   mem_attr_heap_pool_init();

   // Allocate 0x100 bytes of cacheable memory from `mem_cacheable`
   block = mem_attr_heap_alloc(DT_MEM_SW_ALLOC_CACHE, 0x100);

   // Allocate 0x200 bytes of non-cacheable memory aligned to 32 bytes
   // from `mem_non_cacheable`
   block = mem_attr_heap_aligned_alloc(ATTR_SW_ALLOC_NON_CACHE, 0x100, 32);

   // Allocate 0x100 bytes of cacheable and dma-able memory from `mem_cacheable_dma`
   block = mem_attr_heap_alloc(DT_MEM_SW_ALLOC_CACHE | DT_MEM_SW_ALLOC_DMA, 0x100);

When several regions are marked with the same attributes, the memory is allocated:

1. From the regions where the ``zephyr,memory-attr`` property has the requested
   property (or properties).

2. Among the regions as at point 1, from the smallest region if there is any
   unallocated space left for the requested size

3. If there is not enough space, from the next bigger region able to
   accommodate the requested size

The following example shows the point 3:

.. code-block:: c

   // This memory is allocated from `mem_non_cacheable`
   block = mem_attr_heap_alloc(DT_MEM_SW_ALLOC_NON_CACHE, 0x100);

   // This memory is allocated from `mem_cacheable_big`
   block = mem_attr_heap_alloc(DT_MEM_SW_ALLOC_CACHE, 0x5000);

.. note::

    The framework is assuming that the memory regions used to create the heaps
    are usable by the code and available at init time. The user must take of
    initializing and setting the memory area before calling
    :c:func:`mem_attr_heap_pool_init`.

    That means that the region must be correctly configured in terms of MPU /
    MMU (if needed) and that an actual heap can be created out of it, for
    example by leveraging the ``zephyr,memory-region`` property to create a
    proper linker section to accommodate the heap.

API Reference
*************

.. doxygengroup:: memory_attr_interface
.. doxygengroup:: memory_attr_heap
