.. include:: ./../../../../macros.txt .. include:: ./../../../../units.txt .. _DMA: DMA === Module Files ------------ Driver ^^^^^^ - ``src/app/driver/dma/dma.c`` - ``src/app/driver/dma/dma.h`` Configuration ^^^^^^^^^^^^^ - ``src/app/driver/config/dma_cfg.c`` - ``src/app/driver/config/dma_cfg.h`` Unit Test ^^^^^^^^^ - ``tests/unit/app/driver/dma/dma.c`` - ``tests/unit/app/driver/config/dma_cfg.c`` Description ----------- This document explains how to configure a non-cacheable area to work with DMA. Problem ------- The cache of the MCU must be activated, otherwise the performance hit is too high. But if DMA is used in conjunction with the cache, cache coherency problems arise. If a variable X is copied in the cache, it will not be updated in the main memory. Similarly, if X is written in the main memory, it will not be updated in the cache. To solve this problem, a non cacheable area must be declared in the RAM. The arrays read/written by DMA must then be located in this area. Using a non-cacheable area -------------------------- Declaring a non-cacheable area ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Region 3 defines the RAM used by the MCU. For DMA, the last region, region 15, will be used. The corresponding configuration in |halcogen| is shown in :numref:`hcg-non-cacheable`. .. figure:: img/dma_uncached_region.png :alt: Non cacheable area in |halcogen| :name: hcg-non-cacheable :align: center Non cacheable area in |halcogen| The "type" option declares the area as non-cacheable. Adapting the linker script ^^^^^^^^^^^^^^^^^^^^^^^^^^ The next step is the adapt the linker script. In this example, the size of the non-cacheable area is ``4kB``, or ``0x1000``. In the linker script, the initial size declared for the RAM is ``0x7F000``. The size of the non-cacheable area must be subtracted, so the new RAM size is ``0x7E000``. The next region after RAM starts at ``0x08008000``. The start address of the non-cacheable area is then ``0x08008000 - 0x1000 = 0x08007000``. The resulting section of the linker script is .. code-block:: c RAM (RW) : origin=0x08001000 length=0x0007E000 SHAREDRAM (RW) : origin=0x0807F000 length=0x00001000 The non-cacheable area is defined as ``SHAREDRAM``. In the ``SECTIONS`` part of the linker file, the following has to be added: .. code-block:: c /* USER CODE BEGIN (4) */ .sharedRAM : {} > SHAREDRAM /* USER CODE END */ Working with |freertos| ^^^^^^^^^^^^^^^^^^^^^^^ Care must be taken if working with |freertos|: the OS will override the properties of the area defined in |halcogen|. To avoid this, the function ``vPortStoreTaskMPUSettings()`` has to be modified. The code .. code-block:: c xMPUSettings->xRegion[ portNUM_CONFIGURABLE_REGIONS - 1 ].ulRegionBaseAddress = 0x0807F000; xMPUSettings->xRegion[ portNUM_CONFIGURABLE_REGIONS - 1 ].ulRegionSize = portMPU_SIZE_4KB | portMPU_REGION_ENABLE; xMPUSettings->xRegion[ portNUM_CONFIGURABLE_REGIONS - 1 ].ulRegionAttribute = portMPU_PRIV_RW_USER_RW_NOEXEC | portMPU_NORMAL_OINC_SHARED; has to be added. ``portNUM_CONFIGURABLE_REGIONS-1`` means that the last user region is used, as wished in the |halcogen| configuration. Using the defined area in code ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The variables to be put in the non-cacheable area simply have to be defined like this: .. code-block:: c #pragma SET_DATA_SECTION(".sharedRAM") uint16_t ltc_RxPecBuffer[LTC_N_BYTES_FOR_DATA_TRANSMISSION] = {0}; uint16_t ltc_TxPecBuffer[LTC_N_BYTES_FOR_DATA_TRANSMISSION] = {0}; #pragma SET_DATA_SECTION()