mirror of
https://github.com/openmv/openmv.git
synced 2025-09-26 23:09:13 +08:00
298 lines
9.4 KiB
C
298 lines
9.4 KiB
C
/*
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* Copyright (C) 2013-2024 OpenMV, LLC.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*
|
|
* Interface for using extra frame buffer RAM as a stack.
|
|
*/
|
|
#include "py/obj.h"
|
|
#include "py/runtime.h"
|
|
#include "fb_alloc.h"
|
|
#include "framebuffer.h"
|
|
#include "omv_boardconfig.h"
|
|
#include "omv_common.h"
|
|
|
|
extern char _fb_alloc_end;
|
|
static char *pointer = &_fb_alloc_end;
|
|
|
|
#if defined(FB_ALLOC_STATS)
|
|
static uint32_t alloc_bytes;
|
|
static uint32_t alloc_bytes_peak;
|
|
#endif
|
|
|
|
#if defined(OMV_FB_OVERLAY_MEMORY)
|
|
#define FB_OVERLAY_MEMORY_FLAG 0x1
|
|
extern char _fballoc_overlay_end, _fballoc_overlay_start;
|
|
static char *pointer_overlay = &_fballoc_overlay_end;
|
|
#endif
|
|
|
|
// fb_alloc_free_till_mark() will not free past this.
|
|
// Use fb_alloc_free_till_mark_permanent() instead.
|
|
#define FB_PERMANENT_FLAG 0x2
|
|
|
|
char *fb_alloc_sp() {
|
|
return pointer;
|
|
}
|
|
|
|
MP_WEAK NORETURN void fb_alloc_fail() {
|
|
mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Out of fast frame buffer stack memory"));
|
|
}
|
|
|
|
void fb_alloc_init0() {
|
|
pointer = &_fb_alloc_end;
|
|
#if defined(OMV_FB_OVERLAY_MEMORY)
|
|
pointer_overlay = &_fballoc_overlay_end;
|
|
#endif
|
|
}
|
|
|
|
uint32_t fb_avail() {
|
|
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
|
uint32_t temp = pointer - framebuffer_pool_end(fb) - sizeof(uint32_t);
|
|
return (temp < sizeof(uint32_t)) ? 0 : temp;
|
|
}
|
|
|
|
void fb_alloc_mark() {
|
|
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
|
char *new_pointer = pointer - sizeof(uint32_t);
|
|
|
|
// Check if allocation overwrites the framebuffer pixels
|
|
if (new_pointer < framebuffer_pool_end(fb)) {
|
|
nlr_jump(MP_OBJ_TO_PTR(mp_obj_new_exception_msg(&mp_type_MemoryError,
|
|
MP_ERROR_TEXT("Out of fast frame buffer stack memory"))));
|
|
}
|
|
|
|
// fb_alloc does not allow regions which are a size of 0 to be alloced,
|
|
// meaning that the value below is always 8 or more but never 4. So,
|
|
// we will use a size value of 4 as a marker in the alloc stack.
|
|
*((uint32_t *) new_pointer) = sizeof(uint32_t); // Save size.
|
|
pointer = new_pointer;
|
|
#if defined(FB_ALLOC_STATS)
|
|
alloc_bytes = 0;
|
|
alloc_bytes_peak = 0;
|
|
#endif
|
|
}
|
|
|
|
static void int_fb_alloc_free_till_mark(bool free_permanent) {
|
|
// Previously there was a marks counting method used to provide a semaphore lock for this code:
|
|
//
|
|
// https://github.com/openmv/openmv/commit/c982617523766018fda70c15818f643ee8b1fd33
|
|
//
|
|
// This does not really help you in complex memory allocation operations where you want to be
|
|
// able to unwind things until after a certain point. It also did not handle preventing
|
|
// fb_alloc_free_till_mark() from running in recursive call situations (see find_blobs()).
|
|
while (pointer < &_fb_alloc_end) {
|
|
uint32_t size = *((uint32_t *) pointer);
|
|
if ((!free_permanent) && (size & FB_PERMANENT_FLAG)) {
|
|
return;
|
|
}
|
|
size &= ~FB_PERMANENT_FLAG;
|
|
#if defined(OMV_FB_OVERLAY_MEMORY)
|
|
if (size & FB_OVERLAY_MEMORY_FLAG) {
|
|
// Check for fast flag.
|
|
size &= ~FB_OVERLAY_MEMORY_FLAG; // Remove it.
|
|
pointer_overlay += size - sizeof(uint32_t);
|
|
}
|
|
#endif
|
|
pointer += size; // Get size and pop.
|
|
if (size == sizeof(uint32_t)) {
|
|
break; // Break on first marker.
|
|
}
|
|
}
|
|
#if defined(FB_ALLOC_STATS)
|
|
printf("fb_alloc peak memory: %lu\n", alloc_bytes_peak);
|
|
#endif
|
|
}
|
|
|
|
void fb_alloc_free_till_mark() {
|
|
int_fb_alloc_free_till_mark(false);
|
|
}
|
|
|
|
void fb_alloc_mark_permanent() {
|
|
if (pointer < &_fb_alloc_end) {
|
|
*((uint32_t *) pointer) |= FB_PERMANENT_FLAG;
|
|
}
|
|
}
|
|
|
|
void fb_alloc_free_till_mark_past_mark_permanent() {
|
|
int_fb_alloc_free_till_mark(true);
|
|
}
|
|
|
|
// returns null pointer without error if size==0
|
|
void *fb_alloc(uint32_t size, int hints) {
|
|
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
|
|
|
if (!size) {
|
|
return NULL;
|
|
}
|
|
|
|
size = ((size + sizeof(uint32_t) - 1) / sizeof(uint32_t)) * sizeof(uint32_t); // Round Up
|
|
|
|
if (hints & FB_ALLOC_CACHE_ALIGN) {
|
|
size = OMV_ALIGN_TO(size, OMV_ALLOC_ALIGNMENT);
|
|
size += OMV_ALLOC_ALIGNMENT - sizeof(uint32_t);
|
|
}
|
|
|
|
char *result = pointer - size;
|
|
char *new_pointer = result - sizeof(uint32_t);
|
|
|
|
// Check if allocation overwrites the framebuffer pixels
|
|
if (new_pointer < framebuffer_pool_end(fb)) {
|
|
fb_alloc_fail();
|
|
}
|
|
|
|
// size is always 4/8/12/etc. so the value below must be 8 or more.
|
|
*((uint32_t *) new_pointer) = size + sizeof(uint32_t); // Save size.
|
|
pointer = new_pointer;
|
|
|
|
#if defined(FB_ALLOC_STATS)
|
|
alloc_bytes += size;
|
|
if (alloc_bytes > alloc_bytes_peak) {
|
|
alloc_bytes_peak = alloc_bytes;
|
|
}
|
|
printf("fb_alloc %lu bytes\n", size);
|
|
#endif
|
|
|
|
#if defined(OMV_FB_OVERLAY_MEMORY)
|
|
if ((!(hints & FB_ALLOC_PREFER_SIZE))
|
|
&& (((uint32_t) (pointer_overlay - &_fballoc_overlay_start)) >= size)) {
|
|
// Return overlay memory instead.
|
|
pointer_overlay -= size;
|
|
result = pointer_overlay;
|
|
*new_pointer |= FB_OVERLAY_MEMORY_FLAG; // Add flag.
|
|
}
|
|
#endif
|
|
|
|
if (hints & FB_ALLOC_CACHE_ALIGN) {
|
|
int offset = ((uint32_t) result) % OMV_ALLOC_ALIGNMENT;
|
|
if (offset) {
|
|
result += OMV_ALLOC_ALIGNMENT - offset;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// returns null pointer without error if passed size==0
|
|
void *fb_alloc0(uint32_t size, int hints) {
|
|
void *mem = fb_alloc(size, hints);
|
|
memset(mem, 0, size); // does nothing if size is zero.
|
|
return mem;
|
|
}
|
|
|
|
void *fb_alloc_all(uint32_t *size, int hints) {
|
|
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
|
uint32_t temp = pointer - framebuffer_pool_end(fb) - sizeof(uint32_t);
|
|
|
|
if (temp < sizeof(uint32_t)) {
|
|
*size = 0;
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(OMV_FB_OVERLAY_MEMORY)
|
|
if (!(hints & FB_ALLOC_PREFER_SIZE)) {
|
|
*size = (uint32_t) (pointer_overlay - &_fballoc_overlay_start);
|
|
temp = IM_MIN(temp, *size);
|
|
}
|
|
#endif
|
|
|
|
*size = (temp / sizeof(uint32_t)) * sizeof(uint32_t); // Round Down
|
|
|
|
char *result = pointer - *size;
|
|
char *new_pointer = result - sizeof(uint32_t);
|
|
|
|
// size is always 4/8/12/etc. so the value below must be 8 or more.
|
|
*((uint32_t *) new_pointer) = *size + sizeof(uint32_t); // Save size.
|
|
pointer = new_pointer;
|
|
|
|
#if defined(FB_ALLOC_STATS)
|
|
alloc_bytes += *size;
|
|
if (alloc_bytes > alloc_bytes_peak) {
|
|
alloc_bytes_peak = alloc_bytes;
|
|
}
|
|
printf("fb_alloc_all %lu bytes\n", *size);
|
|
#endif
|
|
|
|
#if defined(OMV_FB_OVERLAY_MEMORY)
|
|
if (!(hints & FB_ALLOC_PREFER_SIZE)) {
|
|
// Return overlay memory instead.
|
|
pointer_overlay -= *size;
|
|
result = pointer_overlay;
|
|
*new_pointer |= FB_OVERLAY_MEMORY_FLAG; // Add flag.
|
|
}
|
|
#endif
|
|
|
|
if (hints & FB_ALLOC_CACHE_ALIGN) {
|
|
int offset = ((uint32_t) result) % OMV_ALLOC_ALIGNMENT;
|
|
if (offset) {
|
|
int inc = OMV_ALLOC_ALIGNMENT - offset;
|
|
result += inc;
|
|
*size -= inc;
|
|
}
|
|
|
|
*size = (*size / OMV_ALLOC_ALIGNMENT) * OMV_ALLOC_ALIGNMENT;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// returns null pointer without error if returned size==0
|
|
void *fb_alloc0_all(uint32_t *size, int hints) {
|
|
void *mem = fb_alloc_all(size, hints);
|
|
memset(mem, 0, *size); // does nothing if size is zero.
|
|
return mem;
|
|
}
|
|
|
|
void fb_free() {
|
|
if (pointer < &_fb_alloc_end) {
|
|
uint32_t size = *((uint32_t *) pointer);
|
|
size &= ~FB_PERMANENT_FLAG;
|
|
#if defined(OMV_FB_OVERLAY_MEMORY)
|
|
if (size & FB_OVERLAY_MEMORY_FLAG) {
|
|
// Check for fast flag.
|
|
size &= ~FB_OVERLAY_MEMORY_FLAG; // Remove it.
|
|
pointer_overlay += size - sizeof(uint32_t);
|
|
}
|
|
#endif
|
|
#if defined(FB_ALLOC_STATS)
|
|
alloc_bytes -= size;
|
|
#endif
|
|
pointer += size; // Get size and pop.
|
|
}
|
|
}
|
|
|
|
void fb_free_all() {
|
|
while (pointer < &_fb_alloc_end) {
|
|
uint32_t size = *((uint32_t *) pointer);
|
|
size &= ~FB_PERMANENT_FLAG;
|
|
#if defined(OMV_FB_OVERLAY_MEMORY)
|
|
if (size & FB_OVERLAY_MEMORY_FLAG) {
|
|
// Check for fast flag.
|
|
size &= ~FB_OVERLAY_MEMORY_FLAG; // Remove it.
|
|
pointer_overlay += size - sizeof(uint32_t);
|
|
}
|
|
#endif
|
|
#if defined(FB_ALLOC_STATS)
|
|
alloc_bytes -= size;
|
|
#endif
|
|
pointer += size; // Get size and pop.
|
|
}
|
|
}
|