mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
Add DMA buffer allocator
This commit is contained in:
parent
df15963b6c
commit
069e96f51a
@ -11,6 +11,7 @@ SRCS += $(addprefix alloc/, \
|
|||||||
xalloc.c \
|
xalloc.c \
|
||||||
fb_alloc.c \
|
fb_alloc.c \
|
||||||
umm_malloc.c \
|
umm_malloc.c \
|
||||||
|
dma_alloc.c \
|
||||||
unaligned_memcpy.c \
|
unaligned_memcpy.c \
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
213
src/omv/alloc/dma_alloc.c
Normal file
213
src/omv/alloc/dma_alloc.c
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the OpenMV project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
||||||
|
* Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
||||||
|
*
|
||||||
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
*
|
||||||
|
* DMA buffer allocator.
|
||||||
|
*
|
||||||
|
* This is a very simple dynamic memory allocator for DMA buffers, that
|
||||||
|
* can allocate memory in multiple domains based on the board configuration.
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "py/runtime.h"
|
||||||
|
#include "py/mphal.h"
|
||||||
|
#include "dma_alloc.h"
|
||||||
|
#include "omv_boardconfig.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
typedef union block block_t;
|
||||||
|
union block {
|
||||||
|
struct {
|
||||||
|
uint32_t used; // Set to 1/true if the block is used.
|
||||||
|
uint32_t size; // Size of the memory block in bytes.
|
||||||
|
void *periph; // The peripheral that owns this block.
|
||||||
|
block_t *next; // Pointer to the the next block in chain.
|
||||||
|
};
|
||||||
|
// Make sure that blocks are always aligned to 32 bytes.
|
||||||
|
uint8_t alignment[32];
|
||||||
|
};// Actual block memory starts right after the block header.
|
||||||
|
|
||||||
|
// Note block sizes can Not be adjusted after the first allocation.
|
||||||
|
// When a block is allocated the first time, the block size is fixed to the requested
|
||||||
|
// memory size, when the block is free'd and reused again the same block size is used.
|
||||||
|
// Use multiples of the following min size when allocating blocks to avoid fragmentation.
|
||||||
|
#define MIN_BLOCK_SIZE (128)
|
||||||
|
|
||||||
|
// Get pointer to the block's memory.
|
||||||
|
#define BLOCK_TO_PTR(block)\
|
||||||
|
((void*)(block + 1))
|
||||||
|
|
||||||
|
// Get pointer to the block's header from memory pointer.
|
||||||
|
#define PTR_TO_BLOCK(ptr)\
|
||||||
|
(((block_t *)ptr) - 1)
|
||||||
|
|
||||||
|
// Initialize the first block in a block chain.
|
||||||
|
#define INIT_BLOCK_CHAIN(D) \
|
||||||
|
({ \
|
||||||
|
block_t *block = (block_t *) DMA_MEMORY_##D; \
|
||||||
|
block->used = false; \
|
||||||
|
block->size = OMV_DMA_MEMORY_ ##D ##_SIZE - sizeof(block_t);\
|
||||||
|
block->next = NULL; \
|
||||||
|
block->periph = NULL; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#if defined(OMV_DMA_MEMORY_D1)
|
||||||
|
static uint8_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(DMA_MEMORY_D1[OMV_DMA_MEMORY_D1_SIZE], 16), ".d1_dma_buffer");
|
||||||
|
#endif
|
||||||
|
#if defined(OMV_DMA_MEMORY_D2)
|
||||||
|
static uint8_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(DMA_MEMORY_D2[OMV_DMA_MEMORY_D2_SIZE], 16), ".d2_dma_buffer");
|
||||||
|
#endif
|
||||||
|
#if defined(OMV_DMA_MEMORY_D3)
|
||||||
|
static uint8_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(DMA_MEMORY_D3[OMV_DMA_MEMORY_D3_SIZE], 16), ".d3_dma_buffer");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NORETURN static void dma_alloc_fail(uint32_t size)
|
||||||
|
{
|
||||||
|
mp_raise_msg_varg(&mp_type_MemoryError,
|
||||||
|
MP_ERROR_TEXT("DMA buffer allocation failed while allocating %u bytes"), (uint) size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dma_alloc_init0()
|
||||||
|
{
|
||||||
|
// Initialize the first block in each DMA buffer.
|
||||||
|
#if defined(OMV_DMA_MEMORY_D1)
|
||||||
|
INIT_BLOCK_CHAIN(D1);
|
||||||
|
#endif
|
||||||
|
#if defined(OMV_DMA_MEMORY_D2)
|
||||||
|
INIT_BLOCK_CHAIN(D2);
|
||||||
|
#endif
|
||||||
|
#if defined(OMV_DMA_MEMORY_D3)
|
||||||
|
INIT_BLOCK_CHAIN(D3);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int periph_to_domain(void *periph)
|
||||||
|
{
|
||||||
|
// Mask the lower 16 bits to get the bus base address
|
||||||
|
uint32_t base = ((uint32_t) periph) & 0xFFFF0000;
|
||||||
|
|
||||||
|
switch (base) {
|
||||||
|
#if defined(OMV_DMA_MEMORY_D1)
|
||||||
|
case D1_APB1PERIPH_BASE:
|
||||||
|
case D1_AHB1PERIPH_BASE:
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
|
#if defined(OMV_DMA_MEMORY_D2)
|
||||||
|
case D2_APB1PERIPH_BASE:
|
||||||
|
case D2_APB2PERIPH_BASE:
|
||||||
|
case D2_AHB1PERIPH_BASE:
|
||||||
|
case D2_AHB2PERIPH_BASE:
|
||||||
|
return 2;
|
||||||
|
#endif
|
||||||
|
#if defined(OMV_DMA_MEMORY_D3)
|
||||||
|
case D3_APB1PERIPH_BASE:
|
||||||
|
case D3_AHB1PERIPH_BASE:
|
||||||
|
return 3;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_chain_from_domain(void *periph, void **start, void **end)
|
||||||
|
{
|
||||||
|
// Find the domain this peripheral belongs to.
|
||||||
|
int domain = periph_to_domain(periph);
|
||||||
|
|
||||||
|
switch (domain) {
|
||||||
|
#if defined(OMV_DMA_MEMORY_D1)
|
||||||
|
case 1:
|
||||||
|
*start = DMA_MEMORY_D1;
|
||||||
|
*end = (DMA_MEMORY_D1 + OMV_DMA_MEMORY_D1_SIZE);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(OMV_DMA_MEMORY_D2)
|
||||||
|
case 2:
|
||||||
|
*start = DMA_MEMORY_D2;
|
||||||
|
*end = (DMA_MEMORY_D2 + OMV_DMA_MEMORY_D2_SIZE);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(OMV_DMA_MEMORY_D3)
|
||||||
|
case 3:
|
||||||
|
*start = DMA_MEMORY_D3;
|
||||||
|
*end = (DMA_MEMORY_D3 + OMV_DMA_MEMORY_D3_SIZE);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
mp_raise_msg_varg(&mp_type_MemoryError,
|
||||||
|
MP_ERROR_TEXT("DMA buffer allocation failed. No DMA buffer reserved for peripheral 0x%x"), (uint) periph);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *dma_alloc(uint32_t size, void *periph)
|
||||||
|
{
|
||||||
|
if (size == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round up the alloc memory size to multiples of the minimum block size.
|
||||||
|
size = ((size + MIN_BLOCK_SIZE - 1) / MIN_BLOCK_SIZE) * MIN_BLOCK_SIZE;
|
||||||
|
|
||||||
|
// Find the start and end of the block chain this peripheral belongs to.
|
||||||
|
void *block_chain_start, *block_chain_end;
|
||||||
|
block_chain_from_domain(periph, &block_chain_start, &block_chain_end);
|
||||||
|
|
||||||
|
block_t *block;
|
||||||
|
for (block = block_chain_start; block != NULL; block = block->next) {
|
||||||
|
if (!block->used && block->size >= size) {
|
||||||
|
// Found a free block that's large enough.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block->next == NULL) { // last block
|
||||||
|
// Allocate a new block if there's enough memory left.
|
||||||
|
void *block_end = BLOCK_TO_PTR(block) + block->size;
|
||||||
|
uint32_t freemem = block_chain_end - block_end;
|
||||||
|
if (freemem >= (size + sizeof(block_t))) {
|
||||||
|
block_t *new_block = (block_t *) block_end;
|
||||||
|
new_block->next = NULL; // This is the new last block.
|
||||||
|
block->next = new_block; // Link new block to the old one.
|
||||||
|
block = new_block; // Return the new block.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block == NULL) {
|
||||||
|
dma_alloc_fail(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
block->used = true; // Mark block as used.
|
||||||
|
block->periph = periph;
|
||||||
|
if (block->next == NULL) {
|
||||||
|
// Adjust/set the block size only if it's newly allocated or if it's the first and only
|
||||||
|
// block in the chain, otherwise if it's a free'd/reused block and the size is adjusted,
|
||||||
|
// the block will shrink and it will Not be possible to increase the block size again.
|
||||||
|
block->size = size;
|
||||||
|
}
|
||||||
|
#ifndef NDEBUG
|
||||||
|
printf("alloc DMA buffer at 0x%p size: %lu next in chain: 0x%p for periph 0x%p in domain %d\n",
|
||||||
|
block, block->size, block->next, block->periph, periph_to_domain(block->periph));
|
||||||
|
#endif
|
||||||
|
return BLOCK_TO_PTR(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dma_alloc_free(void *ptr)
|
||||||
|
{
|
||||||
|
if (ptr != NULL) {
|
||||||
|
block_t *block = PTR_TO_BLOCK(ptr);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
printf("free DMA buffer at 0x%p size: %lu next in chain: 0x%p for periph 0x%p in domain %d\n",
|
||||||
|
block, block->size, block->next, block->periph, periph_to_domain(block->periph));
|
||||||
|
#endif
|
||||||
|
// Just mark the block as unused.
|
||||||
|
block->used = false;
|
||||||
|
block->periph = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/omv/alloc/dma_alloc.h
Normal file
17
src/omv/alloc/dma_alloc.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the OpenMV project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
||||||
|
* Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
||||||
|
*
|
||||||
|
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
*
|
||||||
|
* DMA buffer allocator.
|
||||||
|
*/
|
||||||
|
#ifndef __DMA_ALLOC_H__
|
||||||
|
#define __DMA_ALLOC_H__
|
||||||
|
#include <stdint.h>
|
||||||
|
void dma_alloc_init0();
|
||||||
|
void *dma_alloc(uint32_t size, void *periph);
|
||||||
|
void dma_alloc_free(void *ptr);
|
||||||
|
#endif // __DMA_ALLOC_H__
|
||||||
@ -94,6 +94,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/alloc/, \
|
|||||||
xalloc.o \
|
xalloc.o \
|
||||||
fb_alloc.o \
|
fb_alloc.o \
|
||||||
umm_malloc.o \
|
umm_malloc.o \
|
||||||
|
dma_alloc.o \
|
||||||
unaligned_memcpy.o \
|
unaligned_memcpy.o \
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -59,6 +59,7 @@
|
|||||||
#include "wifidbg.h"
|
#include "wifidbg.h"
|
||||||
#include "sdram.h"
|
#include "sdram.h"
|
||||||
#include "fb_alloc.h"
|
#include "fb_alloc.h"
|
||||||
|
#include "dma_alloc.h"
|
||||||
#include "ff_wrapper.h"
|
#include "ff_wrapper.h"
|
||||||
|
|
||||||
#include "usbd_core.h"
|
#include "usbd_core.h"
|
||||||
@ -511,6 +512,7 @@ soft_reset:
|
|||||||
sensor_init0();
|
sensor_init0();
|
||||||
framebuffer_init0();
|
framebuffer_init0();
|
||||||
fb_alloc_init0();
|
fb_alloc_init0();
|
||||||
|
dma_alloc_init0();
|
||||||
#ifdef IMLIB_ENABLE_IMAGE_FILE_IO
|
#ifdef IMLIB_ENABLE_IMAGE_FILE_IO
|
||||||
file_buffer_init0();
|
file_buffer_init0();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -104,6 +104,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/alloc/, \
|
|||||||
xalloc.o \
|
xalloc.o \
|
||||||
fb_alloc.o \
|
fb_alloc.o \
|
||||||
umm_malloc.o \
|
umm_malloc.o \
|
||||||
|
dma_alloc.o \
|
||||||
unaligned_memcpy.o \
|
unaligned_memcpy.o \
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user