aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--02-usart/Makefile.preamble2
-rw-r--r--02-usart/include/arch/arm/arch.h3
-rw-r--r--02-usart/include/arch/x86_64/arch.h6
-rw-r--r--02-usart/include/mem.h23
-rw-r--r--02-usart/src/mem.c195
-rw-r--r--02-usart/tests/test_memory.c251
6 files changed, 463 insertions, 17 deletions
diff --git a/02-usart/Makefile.preamble b/02-usart/Makefile.preamble
index 486bd67..f6e1370 100644
--- a/02-usart/Makefile.preamble
+++ b/02-usart/Makefile.preamble
@@ -6,7 +6,7 @@ CFLAGS?=$(OPT) -mcpu=cortex-m4 -mthumb -g -lgcc -static -nostartfiles -Iinclude
LD_FLAGS?=-T linker/linker_script.ld -nostdlib --cref -Map linker/main.map -static
TEST_PREFIX=x86_64-pc-linux-gnu-
-TEST_CFLAGS=-Iinclude -Iinclude/arch/x86_64 -Itest_harness -g3 -ggdb
+TEST_CFLAGS=-Iinclude -Iinclude/arch/x86_64 -Itest_harness -g3 -ggdb -DFOR_TESTING -Wall
all: _$(PREFIX)_obs/main.elf
diff --git a/02-usart/include/arch/arm/arch.h b/02-usart/include/arch/arm/arch.h
index 569ca77..f140c97 100644
--- a/02-usart/include/arch/arm/arch.h
+++ b/02-usart/include/arch/arm/arch.h
@@ -33,4 +33,7 @@
_Static_assert(sizeof(void*) == sizeof(uint32_t), "Pointers must be 32 bits");
#endif
+extern uint32_t DATA_SEGMENT_START;
+extern uint32_t DATA_SEGMENT_STOP;
+
#endif /* ARCH_H_ */
diff --git a/02-usart/include/arch/x86_64/arch.h b/02-usart/include/arch/x86_64/arch.h
index 258214e..accc449 100644
--- a/02-usart/include/arch/x86_64/arch.h
+++ b/02-usart/include/arch/x86_64/arch.h
@@ -24,4 +24,10 @@
#define SYSTEM_CONFIG_BLOCK_BASE (load_fake_scb__())
#define NVIC_BASE (load_fake_nvic__())
+// Pretend there's a data segement at the start of SRAM1 for more accurate
+// testing.
+#define GHOST_DATA_SEGMENT_SIZE 1234
+#define DATA_SEGMENT_START SRAM1_BASE
+#define DATA_SEGMENT_STOP (SRAM1_BASE + GHOST_DATA_SEGMENT_SIZE)
+
#endif /* ARCH_H_ */
diff --git a/02-usart/include/mem.h b/02-usart/include/mem.h
index aa4ce12..f376e7d 100644
--- a/02-usart/include/mem.h
+++ b/02-usart/include/mem.h
@@ -1,4 +1,27 @@
#ifndef MEM_H_
#define MEM_H_
+#include "arch.h"
+#include <stddef.h>
+
+#define MAX_HEAP_SIZE \
+ ((16384 - (DATA_SEGMENT_STOP - DATA_SEGMENT_START)) / 4 * 4)
+
+/* allocates memory on the head, which is stored in sram2 */
+void* halloc(size_t n);
+
+/* Frees the memory allocated by halloc. */
+void hfree(void* mem);
+
+#ifdef FOR_TESTING
+
+void* debug_halloc_get_next_ptr(void* ptr);
+
+void* debug_halloc_get_prev_ptr(void* ptr);
+
+int debug_halloc_assert_consistency(char* error, size_t len);
+
+#endif
+
+
#endif
diff --git a/02-usart/src/mem.c b/02-usart/src/mem.c
index 65d8b60..0cb9ee4 100644
--- a/02-usart/src/mem.c
+++ b/02-usart/src/mem.c
@@ -3,8 +3,8 @@
#include "common.h"
#ifdef ARCH_STM32L4
-// Provide a definition for memset()
-
+/* Provide a definition for memset() when not provided for the
+ * microcontroller. */
void* memset(void* dest, int c, size_t n)
{
uint8_t c8 = (uint8_t) c;
@@ -18,13 +18,188 @@ void* memset(void* dest, int c, size_t n)
return dest;
}
+#else
+
+void* memset(void* dest, int c, size_t n);
+
#endif
-// void memcpy_(void* dest, const void* src, size_t len)
-// {
-// uint8_t* dest_ = (uint8_t*) dest;
-// uint8_t* src_ = (uint8_t*) src;
-//
-// while ((len--) > 0)
-// *(dest_ ++) = *(src_ ++);
-// }
+typedef uint16_t halloc_off_t;
+
+// The sizes will count the number of WORDS allocated.
+// Since there's a max size of 16k, only 12 bits will be
+// needed for this.
+typedef struct HALLOC_NODE {
+ union {
+ uint32_t header;
+ struct {
+ bool used:1; /* Is this memory in use. */
+ /* Number of words allocated. Does not include the header. */
+ uint16_t size:15;
+ halloc_off_t prev; /* The location of the last block (in WORDS from offest) */
+ } PACKED;
+ };
+
+ uint8_t mem[]; /* The memory to use. */
+} halloc_node_t;
+
+halloc_node_t* halloc_start;
+
+#define halloc_node_next(cur) \
+ ((halloc_node_t*)(((uint8_t*)(cur)) + (((cur)->size + 1) * 4)))
+
+#define halloc_node_prev(cur) halloc_node_at_off(cur->prev)
+
+#define halloc_node_at_off(offset) \
+ ((halloc_node_t*)(((uint8_t*) halloc_start) + (offset) * 4))
+
+#define halloc_node_get_off(node) \
+ (((uint32_t)(((uint8_t*)(node)) - ((uint8_t*)(halloc_start)))) / 4)
+
+#define size_for(n) \
+ (((n) / 4) + ((n) % 4 != 0))
+
+void* halloc(size_t size)
+{
+ if (!halloc_start) {
+ halloc_start = (halloc_node_t*) DATA_SEGMENT_STOP;
+ memset(halloc_start, 0, sizeof(halloc_node_t));
+ halloc_start->size = (MAX_HEAP_SIZE / 4) - 1;
+ }
+
+ size_t realsz = size_for(size); /* Clip the size to the nearest word. */
+ halloc_off_t offset = 0;
+ while (offset < (MAX_HEAP_SIZE / 4)) {
+ halloc_node_t* cur = halloc_node_at_off(offset);
+
+ if (!cur->used && (cur->size >= realsz)) {
+ cur->used = true;
+ size_t orig_size = cur->size;
+ cur->size = realsz;
+
+ if (orig_size > realsz) {
+ /* This halloc node needs to split into two blocks. */
+ halloc_node_t* next = halloc_node_next(cur);
+ next->used = 0;
+ next->size = orig_size - realsz - sizeof(halloc_node_t) / 4;
+ next->prev = offset;
+
+ halloc_node_t* nextnext = halloc_node_next(next);
+ if (halloc_node_get_off(nextnext) < (MAX_HEAP_SIZE / 4)) {
+ nextnext->prev = halloc_node_get_off(next);
+ }
+ }
+
+ return (void*) cur->mem;
+ }
+
+ offset += (sizeof(halloc_node_t) / 4) + cur->size;
+ }
+
+ return NULL;
+}
+
+/* Joins this node with the previous and next nodes if they're free. */
+static void coalesce(halloc_node_t* cur)
+{
+ if (cur->used) return;
+
+ halloc_node_t* next = halloc_node_next(cur);
+ if (!next->used) {
+ cur->size += next->size + sizeof(halloc_node_t) / 4;
+ }
+
+ if (cur != halloc_start) {
+ coalesce(halloc_node_prev(cur));
+ }
+}
+
+void hfree(void* mem)
+{
+ /* TODO this should be a critical section. */
+ if (!mem) {
+ /* Consistent with C, freeing a NULL pointer does nothing. */
+ return;
+ }
+
+ halloc_node_t* header = ((halloc_node_t*) mem) - 1;
+
+ if (!header->used) {
+#ifdef FOR_TESTING
+ assert(header->used);
+#endif
+ return; // TODO make this fail on ARM.
+ }
+
+ header->used = 0;
+ coalesce(header);
+}
+
+#ifdef FOR_TESTING
+
+#include <stdio.h>
+
+void* debug_halloc_get_next_ptr(void* ptr)
+{
+ halloc_node_t* node = ptr - sizeof(halloc_node_t);
+ halloc_node_t* next = halloc_node_next(node);
+
+ return next->mem;
+}
+
+void* debug_halloc_get_prev_ptr(void* ptr)
+{
+ halloc_node_t* node = ptr - sizeof(halloc_node_t);
+ halloc_node_t* prev = halloc_node_prev(node);
+
+ return prev->mem;
+}
+
+/* Tests that we can walk up and down the allocated blocks and that they
+ * are properly aligned. */
+int debug_halloc_assert_consistency(char* error, size_t len)
+{
+ halloc_node_t* cur = halloc_node_at_off(0);
+ size_t total_size = 0;
+ size_t offset = 0;
+ size_t loop_check = 0;
+
+ while(1) {
+ total_size += cur->size + 1;
+
+ halloc_node_t* next = halloc_node_next(cur);
+ if (next == DATA_SEGMENT_STOP + MAX_HEAP_SIZE) {
+ break;
+ } else if (next > (void*)(DATA_SEGMENT_STOP + MAX_HEAP_SIZE)){
+ snprintf(
+ error, len, "Next node points is out of bounds. %p vs max of %p\n",
+ next,
+ (void*)(DATA_SEGMENT_STOP + MAX_HEAP_SIZE));
+ return 1;
+ }
+ cur = next;
+ }
+
+ if (total_size * 4 != MAX_HEAP_SIZE) {
+ snprintf(
+ error, len, "Total recorded size is inconsistent. %d vs %d\n",
+ total_size * 4, MAX_HEAP_SIZE);
+ return 1;
+ }
+
+ while (loop_check < 10000) {
+ halloc_node_t* prev = halloc_node_prev(cur);
+
+ if (prev == halloc_start) {
+ return 0;
+ }
+
+ cur = prev;
+ ++ loop_check;
+ }
+
+ snprintf(error, len, "Loop check failed.\n");
+ return 1;
+}
+
+#endif
diff --git a/02-usart/tests/test_memory.c b/02-usart/tests/test_memory.c
index e62877a..e68977a 100644
--- a/02-usart/tests/test_memory.c
+++ b/02-usart/tests/test_memory.c
@@ -1,12 +1,251 @@
+#ifndef FOR_TESTING
+#define FOR_TESTING
+#endif
+
+#include <stdlib.h>
+
+#include "arch.h"
#include "test_harness.c"
-#include "memory.h"
+#include "common.h"
+#include "mem.h"
+
+extern uint32_t* halloc_start;
+static void wipeout_halloc()
+{
+ memset(halloc_start, 0, 1024);
+ halloc_start = NULL;
+}
+
+struct TEST_STRUCT {
+ uint32_t array[3];
+};
+
+struct TEST_STRUCT2 {
+ uint32_t array[10];
+};
+
+
+static struct TEST_STRUCT* new_test_struct()
+{
+ struct TEST_STRUCT* ret = halloc(sizeof(struct TEST_STRUCT));
+
+ ret->array[0] = 1;
+ ret->array[1] = 2;
+ ret->array[2] = 3;
+
+ return ret;
+}
+
+static struct TEST_STRUCT2* new_test_struct2()
+{
+ struct TEST_STRUCT2* ret = halloc(sizeof(struct TEST_STRUCT2));
+
+ for (int i = 0; i < 10; ++ i) {
+ ret->array[i] = i;
+ }
+
+ return ret;
+}
+
+#define ASSERT_CHAIN(t1, t2) \
+ ASSERT_EQ(V(t1) + sizeof(*t1) + 4, V(t2))
+
+TEST(memory, halloc)
+{
+
+#define V(x) ((void*)(x))
+ struct TEST_STRUCT* test1 = new_test_struct();
+ struct TEST_STRUCT2* test2 = new_test_struct2();
+ struct TEST_STRUCT* test3 = new_test_struct();
+ struct TEST_STRUCT2* test4 = new_test_struct2();
+ struct TEST_STRUCT2* test5 = new_test_struct2();
+
+ ASSERT_TRUE(V(test1) != V(test2));
+ ASSERT_TRUE(V(test2) != V(test3));
+ ASSERT_TRUE(V(test3) != V(test1));
+ ASSERT_TRUE(V(test2) != V(test5));
+ ASSERT_TRUE(V(test4) != V(test5));
+
+ ASSERT_CHAIN(test1, test2);
+ ASSERT_CHAIN(test2, test3);
+ ASSERT_CHAIN(test3, test4);
+ ASSERT_CHAIN(test4, test5);
+
+ wipeout_halloc();
+}
+
+struct UNEVEN_STRUCT {
+ uint8_t arr[5];
+};
+
+struct UNEVEN_STRUCT* new_uneven_struct()
+{
+ struct UNEVEN_STRUCT* ret = halloc(sizeof(struct UNEVEN_STRUCT));
+
+ ret->arr[0] = 1;
+ ret->arr[1] = 2;
+ ret->arr[2] = 3;
+ ret->arr[3] = 4;
+ ret->arr[4] = 5;
-TEST(memory, memcpy)
+ return ret;
+}
+
+#define size_for(n) \
+ (((n) / 4) + ((n) % 4 != 0))
+
+TEST(memory, uneven_halloc)
{
- const char* from = "Hello";
- char to[16];
+ struct UNEVEN_STRUCT* test1 = new_uneven_struct();
+ struct UNEVEN_STRUCT* test2 = new_uneven_struct();
+
+ ASSERT_EQ(V(test1) + 12, test2);
+
+ wipeout_halloc();
+}
+
+TEST(memory, halloc_free)
+{
+ struct TEST_STRUCT* test1 = new_test_struct();
+ struct TEST_STRUCT2* test2 = new_test_struct2();
+ struct TEST_STRUCT* test3 = new_test_struct();
+ struct TEST_STRUCT2* test4 = new_test_struct2();
+ struct TEST_STRUCT2* test5 = new_test_struct2();
+
+ hfree(test2);
+ hfree(test4);
+ hfree(test3);
+ hfree(test1);
+ hfree(test5);
+
+ ASSERT_EQ((*halloc_start >> 1) * 4, MAX_HEAP_SIZE - 4);
+
+ test1 = new_test_struct();
+ test2 = new_test_struct2();
+ test3 = new_test_struct();
+ test4 = new_test_struct2();
+ test5 = new_test_struct2();
+
+ hfree(test1);
+ hfree(test3);
+ hfree(test2);
+ hfree(test4);
+ hfree(test5);
+
+ ASSERT_EQ((*halloc_start >> 1) * 4, MAX_HEAP_SIZE - 4);
+
+ test1 = new_test_struct();
+ test2 = new_test_struct2();
+ test3 = new_test_struct();
+ test4 = new_test_struct2();
+ test5 = new_test_struct2();
+
+ hfree(test4);
+ hfree(test3);
+ hfree(test1);
+ hfree(test2);
+ hfree(test5);
+
+ ASSERT_EQ((*halloc_start >> 1) * 4, MAX_HEAP_SIZE - 4);
+
+ wipeout_halloc();
+}
+
+TEST(memory, halloc_free_alloc2)
+{
+ struct TEST_STRUCT2* test1 = new_test_struct2();
+ struct TEST_STRUCT2* test2 = new_test_struct2();
+
+ hfree(test1);
+
+ struct TEST_STRUCT* test3 = new_test_struct();
+ struct TEST_STRUCT* test4 = new_test_struct();
+
+ ASSERT_EQ(debug_halloc_get_next_ptr(test3), V(test4));
+
+ ASSERT_EQ(
+ // There should be a free block after test4.
+ debug_halloc_get_next_ptr(debug_halloc_get_next_ptr(test4)),
+ V(test2));
+
+ ASSERT_EQ(
+ // There should be a free block after test4.
+ debug_halloc_get_prev_ptr(debug_halloc_get_prev_ptr(test2)),
+ V(test4));
+
+ char buf[1024];
+ if (debug_halloc_assert_consistency(buf, 1024)) {
+ fprintf(stderr, "Consistency check failed.\n");
+ fprintf(stderr, buf);
+ ASSERT_TRUE(false);
+ }
+
+ return 0;
+}
+
+TEST(memory, consistency_stress)
+{
+ if (halloc_start) {
+ wipeout_halloc();
+ }
+
+ int i;
+ void* allocd[500] = { 0 };
+ char buf[1024];
+
+ for (i = 0; i < 500; ++ i) {
+ size_t nalloc = rand() % 20;
+ allocd[i] = halloc(nalloc);
+
+ ASSERT_TRUE(allocd[i]);
+ size_t idx = rand() % 500;
+
+ hfree(allocd[idx]);
+ allocd[idx] = NULL;
+
+ idx = rand() % 500;
+ hfree(allocd[idx]);
+ allocd[idx] = NULL;
+
+ if (debug_halloc_assert_consistency(buf, 1024)) {
+ fprintf(stderr, "Consistency check failed. (At index %d) nalloc=%lu\n", i, nalloc);
+ fprintf(stderr, buf);
+ ASSERT_TRUE(false);
+ }
+ }
+
+ for(i = 0; i < 500; ++ i) {
+ hfree(allocd[i]);
+ }
+
+ if (debug_halloc_assert_consistency(buf, 1024)) {
+ fprintf(stderr, "Consistency check failed. (Final)\n");
+ fprintf(stderr, buf);
+ ASSERT_TRUE(false);
+ }
+ ASSERT_EQ((*halloc_start >> 1) * 4, MAX_HEAP_SIZE - 4);
+}
+
+TEST(memory, halloc_free_alloc)
+{
+ new_test_struct();
+ struct TEST_STRUCT2* test2 = new_test_struct2();
+ new_test_struct();
+ struct TEST_STRUCT2* test4 = new_test_struct2();
+ new_test_struct2();
+
+ hfree(test4);
+
+ struct TEST_STRUCT2* test6 = new_test_struct2();
+
+ // test_6 should have been allocated in test_4's spot.
+ ASSERT_EQ(test6, test4);
- memcpy(to, from, 6);
+ hfree(test2);
+ struct TEST_STRUCT* test7 = new_test_struct();
+ struct TEST_STRUCT* test8 = new_test_struct();
- ASSERT_EQ_STR(to, from);
+ // Test 2 was large enough to accomodate 3 smaller structs.
+ ASSERT_EQ(V(test7), V(test2));
+ ASSERT_EQ(V(test8), V(test2) + sizeof(*test7) + 4);
}