circuitpython/atmel-samd/asf/common/utils/membag/membag.c
Scott Shawcroft 8f26d181c3 Blinking the LED works. Clocks should be set up correctly.
Everything works fine without USB being plugged in but faults (I think) when USB is plugged in. This is switched away from the USB code from the bootloader onto the USB code thats generated by Atmel Studio using the high level classes from ASF.
2016-08-22 23:53:11 -07:00

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;
}
}
}