change(system): heap_caps_alloc returns aligned memory if caps indicate a need for it

The implicit promise of heap_alloc_caps() and friends is that the memory it
returns is fit for the purpose as requested in the caps field. Before
this commit, that did not happen; e.g. DMA-capable memory wass returned
from a correct region, but not aligned/sized to something the DMA subsystem
can handle.

This commit adds an API to the esp_mm component that is then used by the
heap component to adjust allocation alignment, caps and size dependent on
the hardware requirement of the requested allocation caps.
This commit is contained in:
Jeroen Domburg
2024-04-23 12:59:39 +08:00
parent 3f632df143
commit df4195062d
15 changed files with 323 additions and 53 deletions

View File

@@ -1,5 +1,6 @@
set(src_test "test_heap_main.c"
"test_aligned_alloc_caps.c"
"test_heap_align_hw.c"
"test_allocator_timings.c"
"test_corruption_check.c"
"test_diram.c"
@@ -13,5 +14,5 @@ set(src_test "test_heap_main.c"
idf_component_register(SRCS ${src_test}
INCLUDE_DIRS "."
REQUIRES unity esp_psram spi_flash
REQUIRES unity esp_psram spi_flash esp_mm
WHOLE_ARCHIVE)

View File

@@ -0,0 +1,87 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <sys/param.h>
#include <string.h>
#include "inttypes.h"
#include "esp_log.h"
#include "esp_attr.h"
#include "unity.h"
#include "esp_private/esp_cache_private.h"
#include "esp_memory_utils.h"
#include "hal/cache_ll.h"
#include "hal/cache_hal.h"
TEST_CASE("test heap_caps_malloc_prefer for dma memory", "[hw-align]")
{
void *ptr = NULL;
ptr = heap_caps_malloc_prefer(40, 1, MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(ptr);
TEST_ASSERT(esp_ptr_dma_capable(ptr));
free(ptr);
}
TEST_CASE("test heap_caps_calloc_prefer for dma memory", "[hw-align]")
{
void *ptr = NULL;
ptr = heap_caps_calloc_prefer(40, 1, 1, MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(ptr);
TEST_ASSERT(esp_ptr_dma_capable(ptr));
free(ptr);
}
#include "esp_private/heap_align_hw.h"
#define TEST_ALLOC_COUNT 100
static bool test_alignment(uint32_t caps, int expected_alignment) {
bool ret=true;
void *mem[TEST_ALLOC_COUNT];
size_t size[TEST_ALLOC_COUNT];
//First, check if we can allocate memory with these caps anyway.
void *tst=heap_caps_malloc(1, caps);
if (!tst) return true;
free(tst);
//Step 1: generate sizes and allocate memory.
for (int i=0; i<TEST_ALLOC_COUNT; i++) {
size[i]=rand()&128;
mem[i]=heap_caps_malloc(size[i], caps);
}
// Step 2: check alignment and fill up memory up to the aligned size
// (which should succeed as we expect to get an integer amount of cache lines)
for (int i=0; i<TEST_ALLOC_COUNT; i++) {
intptr_t off=(intptr_t)mem[i];
if (off&(expected_alignment-1)) ret=false;
size_t size_aligned_up = (size[i] + expected_alignment - 1) & (~(expected_alignment - 1));
memset(mem[i], 0xA5, size_aligned_up);
}
//Step 3: Free the memory again.
//This should not lead to heap corruption (from the memset) being detected.
for (int i=0; i<TEST_ALLOC_COUNT; i++) {
free(mem[i]);
}
return ret;
}
TEST_CASE("test alignment for dma", "[hw-align]")
{
int int_cache_size=cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
int ext_cache_size=cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
test_alignment(MALLOC_CAP_DMA, int_cache_size);
test_alignment(MALLOC_CAP_DMA_DESC_AHB, int_cache_size);
test_alignment(MALLOC_CAP_DMA_DESC_AXI, int_cache_size>8?int_cache_size:8);
test_alignment(MALLOC_CAP_DMA|MALLOC_CAP_SPIRAM, ext_cache_size);
}