326 lines
8.8 KiB
C
326 lines
8.8 KiB
C
/**
|
|
* \file
|
|
*
|
|
* \brief Memory bag allocator
|
|
*
|
|
* Copyright (C) 2012-2015 Atmel Corporation. All rights reserved.
|
|
*
|
|
* \asf_license_start
|
|
*
|
|
* \page License
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3. The name of Atmel may not be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* 4. This software may only be redistributed and used in connection with an
|
|
* Atmel microcontroller product.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
|
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* \asf_license_stop
|
|
*
|
|
*/
|
|
/*
|
|
* Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
|
|
*/
|
|
|
|
#include <compiler.h>
|
|
#include <membag.h>
|
|
#include "conf_membag.h"
|
|
|
|
/** \internal
|
|
*
|
|
* Retrieves the number of elements in a statically declared array.
|
|
*/
|
|
#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
|
|
|
|
/**
|
|
* Static address space which is split up into usable chunks by membag.
|
|
* For configuration details, see \ref membag_list.
|
|
*/
|
|
static uint8_t membag_pool[CONF_MEMBAG_POOL_SIZE];
|
|
|
|
/**
|
|
* Internal structure used by membag to keep track of memory,
|
|
* with maximum 32 blocks per membag.
|
|
*/
|
|
struct membag {
|
|
/*! Number of bytes per block in this bag. */
|
|
size_t block_size;
|
|
/*! Total number of blocks. */
|
|
size_t num_blocks;
|
|
/*! Pointer to start of this bag. */
|
|
uintptr_t start;
|
|
/*! Pointer to end of this bag. */
|
|
uintptr_t end;
|
|
/*! 32-bit integer used to keep track of allocations. */
|
|
uint32_t allocated;
|
|
/*! Counter for number of free blocks. */
|
|
uint8_t blocks_free;
|
|
};
|
|
|
|
/**
|
|
* Array of available membags, provided by the user in the applications
|
|
* conf_membag.h header file. Example:
|
|
*
|
|
* \code
|
|
#define CONF_MEMBAG_ARRAY \
|
|
MEMBAG(32, 4), \
|
|
MEMBAG(16, 2),
|
|
|
|
#define CONF_MEMBAG_POOL_SIZE \
|
|
MEMBAG_SIZE(32, 4) + \
|
|
MEMBAG_SIZE(16, 2)
|
|
\endcode
|
|
*
|
|
*/
|
|
static struct membag membag_list[] = {
|
|
CONF_MEMBAG_ARRAY
|
|
};
|
|
|
|
/**
|
|
* \brief Initialize the membag system.
|
|
*
|
|
* This function sets up the membags, allocates memory from the memory pool, and
|
|
* initializes them. Any existing allocations are destroyed and all memory pools
|
|
* reset to their initial states.
|
|
*/
|
|
void membag_init(void)
|
|
{
|
|
uint8_t i;
|
|
uintptr_t poolptr;
|
|
|
|
poolptr = (uintptr_t)membag_pool;
|
|
|
|
for (i = 0; i < ARRAY_LEN(membag_list); i++) {
|
|
Assert(membag_list[i].block_size > 0);
|
|
Assert(membag_list[i].num_blocks > 0);
|
|
Assert(membag_list[i].num_blocks <= 32);
|
|
|
|
membag_list[i].start = poolptr;
|
|
poolptr += (membag_list[i].block_size *
|
|
membag_list[i].num_blocks);
|
|
membag_list[i].end = poolptr;
|
|
membag_list[i].blocks_free = membag_list[i].num_blocks;
|
|
|
|
/* Mark all blocks as free. */
|
|
membag_list[i].allocated = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Determine the total remaining free memory from all membags.
|
|
*
|
|
* \return Sum of all free memory, in bytes.
|
|
*/
|
|
size_t membag_get_total_free(void)
|
|
{
|
|
uint8_t i;
|
|
size_t total_free = 0;
|
|
|
|
for (i = 0; i < ARRAY_LEN(membag_list); i++) {
|
|
total_free += membag_list[i].blocks_free *
|
|
membag_list[i].block_size;
|
|
}
|
|
|
|
return total_free;
|
|
}
|
|
|
|
/**
|
|
* \brief Determine the total memory from all membags.
|
|
*
|
|
* \return Sum of all blocks in all bags, in bytes.
|
|
*/
|
|
size_t membag_get_total(void)
|
|
{
|
|
uint8_t i;
|
|
size_t total = 0;
|
|
|
|
for (i = 0; i < ARRAY_LEN(membag_list); i++) {
|
|
total += membag_list[i].num_blocks * membag_list[i].block_size;
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
/**
|
|
* \brief Determine the smallest available block size.
|
|
*
|
|
* Calculates the smallest block which can be allocated by the Membag allocator
|
|
* if requested. Allocations larger than this amount are not guaranteed to
|
|
* complete successfully.
|
|
*
|
|
* \return Size of the smallest available block, in bytes.
|
|
*/
|
|
size_t membag_get_smallest_free_block_size(void)
|
|
{
|
|
uint8_t i;
|
|
struct membag *smallest_bag = NULL;
|
|
|
|
for (i = 0; i < ARRAY_LEN(membag_list); i++) {
|
|
if (membag_list[i].blocks_free == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (!smallest_bag ||
|
|
(smallest_bag->block_size > membag_list[i].block_size)) {
|
|
smallest_bag = &membag_list[i];
|
|
}
|
|
}
|
|
|
|
if (smallest_bag) {
|
|
return smallest_bag->block_size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Determine the largest available block size.
|
|
*
|
|
* Calculates the largest block which can be allocated by the Membag allocator
|
|
* if requested. Allocations larger than this amount are guaranteed to fail.
|
|
*
|
|
* \return Size of the largest available block, in bytes.
|
|
*/
|
|
size_t membag_get_largest_free_block_size(void)
|
|
{
|
|
uint8_t i;
|
|
struct membag *largest_bag = NULL;
|
|
|
|
for (i = 0; i < ARRAY_LEN(membag_list); i++) {
|
|
if (membag_list[i].blocks_free == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (!largest_bag ||
|
|
(largest_bag->block_size < membag_list[i].block_size)) {
|
|
largest_bag = &membag_list[i];
|
|
}
|
|
}
|
|
|
|
if (largest_bag) {
|
|
return largest_bag->block_size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Allocate a memory block via a block from the Membag pool
|
|
*
|
|
* Allocates memory to the user from one of the available Membag pools. Each
|
|
* Membag pool is examined in sequence, and the first free block of sufficient
|
|
* size (if any) is chosen for the allocation. Allocated blocks persist until
|
|
* either the Membag module is re-initialized, or an allocation block is freed
|
|
* via \ref membag_free().
|
|
*
|
|
* \note The execution cycle time for this function is not deterministic; each
|
|
* allocation request may take a variable amount of cycles to complete.
|
|
*
|
|
* \param size Size of memory block requested, in bytes
|
|
*
|
|
* \return Pointer to the start of an allocated block if one was found in the
|
|
* Membag pool, NULL if no suitable block was found.
|
|
*/
|
|
void *membag_alloc(const size_t size)
|
|
{
|
|
uint8_t i;
|
|
struct membag *smallest_bag = NULL;
|
|
uintptr_t p;
|
|
|
|
/* Find the smallest available block size big enough for the requested
|
|
* memory chunk size. */
|
|
for (i = 0; i < ARRAY_LEN(membag_list); i++) {
|
|
if (membag_list[i].blocks_free == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (membag_list[i].block_size >= size) {
|
|
if (!smallest_bag ||
|
|
(smallest_bag->block_size > membag_list[i].block_size)) {
|
|
smallest_bag = &membag_list[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We return the first available block in the bag that has one, and if
|
|
* there is none, we return NULL.
|
|
*/
|
|
if (smallest_bag) {
|
|
/* We know that there is a free block within the membag's
|
|
* memory, and we simply return the first one available.
|
|
*/
|
|
p = smallest_bag->start;
|
|
|
|
for (i = 0; i < smallest_bag->num_blocks; i++) {
|
|
/* Check the allocation byte to see whether the block is
|
|
* in use. */
|
|
if (!(smallest_bag->allocated & ((uint32_t)1 << i))) {
|
|
/* It is free, set it to used. */
|
|
smallest_bag->allocated |= ((uint32_t)1 << i);
|
|
smallest_bag->blocks_free--;
|
|
|
|
return (void *)(p);
|
|
}
|
|
|
|
p += smallest_bag->block_size;
|
|
}
|
|
}
|
|
|
|
/* There is no available memory. Return NULL. */
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* \brief Free a previously allocated memory block from the Membag pool
|
|
*
|
|
* This function frees memory which has been allocated previously via a
|
|
* successful call to \ref membag_alloc(). Once deallocated, the given pointer
|
|
* is no longer valid and should not be used in the user application unless
|
|
* re-allocated.
|
|
*
|
|
* \note The execution cycle time for this function is not deterministic; each
|
|
* allocation request may take a variable amount of cycles to complete.
|
|
*
|
|
* \param ptr Pointer to an allocated memory block to free
|
|
*/
|
|
void membag_free(const void *ptr)
|
|
{
|
|
uint8_t i;
|
|
uintptr_t p = (uintptr_t)ptr;
|
|
uint8_t block_index;
|
|
|
|
for (i = 0; i < ARRAY_LEN(membag_list); i++) {
|
|
if (p >= membag_list[i].start && p < membag_list[i].end) {
|
|
block_index = (p - membag_list[i].start) / membag_list[i].block_size;
|
|
|
|
/* Mark the memory as free. */
|
|
membag_list[i].allocated &= ~((uint32_t)1 << block_index);
|
|
membag_list[i].blocks_free++;
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|