[Feature]add MT2731_MP2_MR2_SVN388 baseline version
Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/bsp/lk/app/tests/benchmarks.c b/src/bsp/lk/app/tests/benchmarks.c
new file mode 100644
index 0000000..30de163
--- /dev/null
+++ b/src/bsp/lk/app/tests/benchmarks.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2008-2012 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <rand.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <app/tests.h>
+#include <kernel/thread.h>
+#include <kernel/mutex.h>
+#include <kernel/semaphore.h>
+#include <kernel/event.h>
+#include <platform.h>
+
+const size_t BUFSIZE = (1024*1024);
+const uint ITER = 1024;
+
+__NO_INLINE static void bench_set_overhead(void)
+{
+ uint32_t *buf = malloc(BUFSIZE);
+
+ uint count = arch_cycle_count();
+ for (uint i = 0; i < ITER; i++) {
+ __asm__ volatile("");
+ }
+ count = arch_cycle_count() - count;
+
+ printf("took %u cycles overhead to loop %u times\n",
+ count, ITER);
+
+ free(buf);
+}
+
+__NO_INLINE static void bench_memset(void)
+{
+ void *buf = malloc(BUFSIZE);
+
+ uint count = arch_cycle_count();
+ for (uint i = 0; i < ITER; i++) {
+ memset(buf, 0, BUFSIZE);
+ }
+ count = arch_cycle_count() - count;
+
+ printf("took %u cycles to memset a buffer of size %u %d times (%u bytes), %f bytes/cycle\n",
+ count, BUFSIZE, ITER, BUFSIZE * ITER, (BUFSIZE * ITER) / (float)count);
+
+ free(buf);
+}
+
+#define bench_cset(type) \
+__NO_INLINE static void bench_cset_##type(void) \
+{ \
+ type *buf = malloc(BUFSIZE); \
+ \
+ uint count = arch_cycle_count(); \
+ for (uint i = 0; i < ITER; i++) { \
+ for (uint j = 0; j < BUFSIZE / sizeof(*buf); j++) { \
+ buf[j] = 0; \
+ } \
+ } \
+ count = arch_cycle_count() - count; \
+ \
+ printf("took %u cycles to manually clear a buffer using wordsize %d of size %u %d times (%u bytes), %f bytes/cycle\n", \
+ count, sizeof(*buf), BUFSIZE, ITER, BUFSIZE * ITER, (BUFSIZE * ITER) / (float)count); \
+ \
+ free(buf); \
+}
+
+bench_cset(uint8_t)
+bench_cset(uint16_t)
+bench_cset(uint32_t)
+bench_cset(uint64_t)
+
+__NO_INLINE static void bench_cset_wide(void)
+{
+ uint32_t *buf = malloc(BUFSIZE);
+
+ uint count = arch_cycle_count();
+ for (uint i = 0; i < ITER; i++) {
+ for (uint j = 0; j < BUFSIZE / sizeof(*buf) / 8; j++) {
+ buf[j*8] = 0;
+ buf[j*8+1] = 0;
+ buf[j*8+2] = 0;
+ buf[j*8+3] = 0;
+ buf[j*8+4] = 0;
+ buf[j*8+5] = 0;
+ buf[j*8+6] = 0;
+ buf[j*8+7] = 0;
+ }
+ }
+ count = arch_cycle_count() - count;
+
+ printf("took %u cycles to manually clear a buffer of size %u %d times 8 words at a time (%u bytes), %f bytes/cycle\n",
+ count, BUFSIZE, ITER, BUFSIZE * ITER, (BUFSIZE * ITER) / (float)count);
+
+ free(buf);
+}
+
+__NO_INLINE static void bench_memcpy(void)
+{
+ uint8_t *buf = calloc(1, BUFSIZE);
+
+ uint count = arch_cycle_count();
+ for (uint i = 0; i < ITER; i++) {
+ memcpy(buf, buf + BUFSIZE / 2, BUFSIZE / 2);
+ }
+ count = arch_cycle_count() - count;
+
+ printf("took %u cycles to memcpy a buffer of size %u %d times (%u source bytes), %f source bytes/cycle\n",
+ count, BUFSIZE / 2, ITER, BUFSIZE / 2 * ITER, (BUFSIZE / 2 * ITER) / (float)count);
+
+ free(buf);
+}
+
+#if ARCH_ARM
+__NO_INLINE static void arm_bench_cset_stm(void)
+{
+ uint32_t *buf = malloc(BUFSIZE);
+
+ uint count = arch_cycle_count();
+ for (uint i = 0; i < ITER; i++) {
+ for (uint j = 0; j < BUFSIZE / sizeof(*buf) / 8; j++) {
+ __asm__ volatile(
+ "stm %0, {r0-r7};"
+ :: "r" (&buf[j*8])
+ );
+ }
+ }
+ count = arch_cycle_count() - count;
+
+ printf("took %u cycles to manually clear a buffer of size %u %d times 8 words at a time using stm (%u bytes), %f bytes/cycle\n",
+ count, BUFSIZE, ITER, BUFSIZE * ITER, (BUFSIZE * ITER) / (float)count);
+
+ free(buf);
+}
+
+#if (__CORTEX_M >= 0x03)
+__NO_INLINE static void arm_bench_multi_issue(void)
+{
+ uint32_t cycles;
+ uint32_t a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0;
+#define ITER 1000000
+ uint count = ITER;
+ cycles = arch_cycle_count();
+ while (count--) {
+ asm volatile ("");
+ asm volatile ("add %0, %0, %0" : "=r" (a) : "r" (a));
+ asm volatile ("add %0, %0, %0" : "=r" (b) : "r" (b));
+ asm volatile ("and %0, %0, %0" : "=r" (c) : "r" (c));
+ asm volatile ("mov %0, %0" : "=r" (d) : "r" (d));
+ asm volatile ("orr %0, %0, %0" : "=r" (e) : "r" (e));
+ asm volatile ("add %0, %0, %0" : "=r" (f) : "r" (f));
+ asm volatile ("and %0, %0, %0" : "=r" (g) : "r" (g));
+ asm volatile ("mov %0, %0" : "=r" (h) : "r" (h));
+ }
+ cycles = arch_cycle_count() - cycles;
+
+ printf("took %u cycles to issue 8 integer ops (%f cycles/iteration)\n", cycles, (float)cycles / ITER);
+#undef ITER
+}
+#endif // __CORTEX_M
+#endif // ARCH_ARM
+
+#if WITH_LIB_LIBM
+#include <math.h>
+
+__NO_INLINE static void bench_sincos(void)
+{
+ printf("touching the floating point unit\n");
+ __UNUSED volatile double _hole = sin(0);
+
+ uint count = arch_cycle_count();
+ __UNUSED double a = sin(2.0);
+ count = arch_cycle_count() - count;
+ printf("took %u cycles for sin()\n", count);
+
+ count = arch_cycle_count();
+ a = cos(2.0);
+ count = arch_cycle_count() - count;
+ printf("took %u cycles for cos()\n", count);
+
+ count = arch_cycle_count();
+ a = sinf(2.0);
+ count = arch_cycle_count() - count;
+ printf("took %u cycles for sinf()\n", count);
+
+ count = arch_cycle_count();
+ a = cosf(2.0);
+ count = arch_cycle_count() - count;
+ printf("took %u cycles for cosf()\n", count);
+
+ count = arch_cycle_count();
+ a = sqrt(1234567.0);
+ count = arch_cycle_count() - count;
+ printf("took %u cycles for sqrt()\n", count);
+
+ count = arch_cycle_count();
+ a = sqrtf(1234567.0f);
+ count = arch_cycle_count() - count;
+ printf("took %u cycles for sqrtf()\n", count);
+}
+
+#endif // WITH_LIB_LIBM
+
+int benchmarks(int argc, const cmd_args *argv)
+{
+ bench_set_overhead();
+ bench_memset();
+ bench_memcpy();
+
+ bench_cset_uint8_t();
+ bench_cset_uint16_t();
+ bench_cset_uint32_t();
+ bench_cset_uint64_t();
+ bench_cset_wide();
+
+#if ARCH_ARM
+ arm_bench_cset_stm();
+
+#if (__CORTEX_M >= 0x03)
+ arm_bench_multi_issue();
+#endif
+#endif
+#if WITH_LIB_LIBM
+ bench_sincos();
+#endif
+
+ return NO_ERROR;
+}
+
diff --git a/src/bsp/lk/app/tests/cache_tests.c b/src/bsp/lk/app/tests/cache_tests.c
new file mode 100644
index 0000000..46c8a93
--- /dev/null
+++ b/src/bsp/lk/app/tests/cache_tests.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#if ARM_WITH_CACHE
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arch.h>
+#include <arch/ops.h>
+#include <lib/console.h>
+#include <platform.h>
+
+static void bench_cache(size_t bufsize, uint8_t* buf)
+{
+ lk_bigtime_t t;
+ bool do_free;
+
+ if (buf == 0) {
+ buf = memalign(PAGE_SIZE, bufsize);
+ do_free = true;
+ } else {
+ do_free = false;
+ }
+
+ printf("buf %p, size %zu\n", buf, bufsize);
+
+ if (!buf)
+ return;
+
+ t = current_time_hires();
+ arch_clean_cache_range((addr_t)buf, bufsize);
+ t = current_time_hires() - t;
+
+ printf("took %llu usecs to clean %d bytes (cold)\n", t, bufsize);
+
+ memset(buf, 0x99, bufsize);
+
+ t = current_time_hires();
+ arch_clean_cache_range((addr_t)buf, bufsize);
+ t = current_time_hires() - t;
+
+ if (do_free)
+ free(buf);
+
+ printf("took %llu usecs to clean %d bytes (hot)\n", t, bufsize);
+}
+
+static int cache_tests(int argc, const cmd_args *argv)
+{
+ uint8_t* buf;
+ buf = (uint8_t *)((argc > 1) ? argv[1].u : 0UL);
+
+ printf("testing cache\n");
+
+ bench_cache(2*1024, buf);
+ bench_cache(64*1024, buf);
+ bench_cache(256*1024, buf);
+ bench_cache(1*1024*1024, buf);
+ bench_cache(8*1024*1024, buf);
+ return 0;
+}
+
+STATIC_COMMAND_START
+STATIC_COMMAND("cache_tests", "test/bench the cpu cache", &cache_tests)
+STATIC_COMMAND_END(cache_tests);
+
+#endif
diff --git a/src/bsp/lk/app/tests/clock_tests.c b/src/bsp/lk/app/tests/clock_tests.c
new file mode 100644
index 0000000..b5e7f74
--- /dev/null
+++ b/src/bsp/lk/app/tests/clock_tests.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2012 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#include <stdio.h>
+#include <rand.h>
+#include <err.h>
+#include <app/tests.h>
+#include <kernel/thread.h>
+#include <kernel/mutex.h>
+#include <kernel/semaphore.h>
+#include <kernel/event.h>
+#include <platform.h>
+
+int clock_tests(int argc, const cmd_args *argv)
+{
+ uint32_t c;
+ lk_time_t t;
+ lk_bigtime_t t2;
+
+ thread_sleep(100);
+ c = arch_cycle_count();
+ t = current_time();
+ c = arch_cycle_count() - c;
+ printf("%u cycles per current_time()\n", c);
+
+ thread_sleep(100);
+ c = arch_cycle_count();
+ t2 = current_time_hires();
+ c = arch_cycle_count() - c;
+ printf("%u cycles per current_time_hires()\n", c);
+
+ printf("making sure time never goes backwards\n");
+ {
+ printf("testing current_time()\n");
+ lk_time_t start = current_time();
+ lk_time_t last = start;
+ for (;;) {
+ t = current_time();
+ //printf("%lu %lu\n", last, t);
+ if (TIME_LT(t, last)) {
+ printf("WARNING: time ran backwards: %lu < %lu\n", t, last);
+ last = t;
+ continue;
+ }
+ last = t;
+ if (last - start > 5000)
+ break;
+ }
+ }
+ {
+ printf("testing current_time_hires()\n");
+ lk_bigtime_t start = current_time_hires();
+ lk_bigtime_t last = start;
+ for (;;) {
+ t2 = current_time_hires();
+ //printf("%llu %llu\n", last, t2);
+ if (t2 < last) {
+ printf("WARNING: time ran backwards: %llu < %llu\n", t2, last);
+ last = t2;
+ continue;
+ }
+ last = t2;
+ if (last - start > 5000000)
+ break;
+ }
+ }
+
+ printf("making sure current_time() and current_time_hires() are always the same base\n");
+ {
+ lk_time_t start = current_time();
+ for (;;) {
+ t = current_time();
+ t2 = current_time_hires();
+ if (t > ((t2 + 500) / 1000)) {
+ printf("WARNING: current_time() ahead of current_time_hires() %lu %llu\n", t, t2);
+ }
+ if (t - start > 5000)
+ break;
+ }
+ }
+
+ printf("counting to 5, in one second intervals\n");
+ for (int i = 0; i < 5; i++) {
+ thread_sleep(1000);
+ printf("%d\n", i + 1);
+ }
+
+ printf("measuring cpu clock against current_time_hires()\n");
+ for (int i = 0; i < 5; i++) {
+ uint cycles = arch_cycle_count();
+ lk_bigtime_t start = current_time_hires();
+ while ((current_time_hires() - start) < 1000000)
+ ;
+ cycles = arch_cycle_count() - cycles;
+ printf("%u cycles per second\n", cycles);
+ }
+
+ return NO_ERROR;
+}
+
+// vim: set noexpandtab:
diff --git a/src/bsp/lk/app/tests/fibo.c b/src/bsp/lk/app/tests/fibo.c
new file mode 100644
index 0000000..3c3320f
--- /dev/null
+++ b/src/bsp/lk/app/tests/fibo.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2012 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#include <stdio.h>
+#include <rand.h>
+#include <err.h>
+#include <app/tests.h>
+#include <kernel/thread.h>
+#include <kernel/mutex.h>
+#include <kernel/semaphore.h>
+#include <kernel/event.h>
+#include <platform.h>
+
+static int fibo_thread(void *argv)
+{
+ long fibo = (intptr_t)argv;
+
+ thread_t *t[2];
+
+ if (fibo == 0)
+ return 0;
+ if (fibo == 1)
+ return 1;
+
+ char name[32];
+ snprintf(name, sizeof(name), "fibo %lu", fibo - 1);
+ t[0] = thread_create(name, &fibo_thread, (void *)(fibo - 1), DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ if (!t[0]) {
+ printf("error creating thread for fibo %d\n", fibo-1);
+ return 0;
+ }
+ snprintf(name, sizeof(name), "fibo %lu", fibo - 2);
+ t[1] = thread_create(name, &fibo_thread, (void *)(fibo - 2), DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ if (!t[1]) {
+ printf("error creating thread for fibo %d\n", fibo-2);
+ thread_resume(t[0]);
+ thread_join(t[0], NULL, INFINITE_TIME);
+ return 0;
+ }
+
+ thread_resume(t[0]);
+ thread_resume(t[1]);
+
+ int retcode0, retcode1;
+
+ thread_join(t[0], &retcode0, INFINITE_TIME);
+ thread_join(t[1], &retcode1, INFINITE_TIME);
+
+ return retcode0 + retcode1;
+}
+
+int fibo(int argc, const cmd_args *argv)
+{
+
+ if (argc < 2) {
+ printf("not enough args\n");
+ return -1;
+ }
+
+ lk_time_t tim = current_time();
+
+ thread_t *t = thread_create("fibo", &fibo_thread, (void *)(uintptr_t)argv[1].u, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_resume(t);
+
+ int retcode;
+ thread_join(t, &retcode, INFINITE_TIME);
+
+ tim = current_time() - tim;
+
+ printf("fibo %d\n", retcode);
+ printf("took %u msecs to calculate\n", tim);
+
+ return NO_ERROR;
+}
+
+// vim: set noexpandtab:
+
diff --git a/src/bsp/lk/app/tests/float.c b/src/bsp/lk/app/tests/float.c
new file mode 100755
index 0000000..5d3d8c6
--- /dev/null
+++ b/src/bsp/lk/app/tests/float.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2013-2015 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#if ARM_WITH_VFP || ARCH_ARM64 || X86_WITH_FPU
+
+#include <stdio.h>
+#include <rand.h>
+#include <err.h>
+#include <lib/console.h>
+#include <app/tests.h>
+#include <kernel/thread.h>
+#include <kernel/mutex.h>
+#include <kernel/semaphore.h>
+#include <kernel/event.h>
+#include <platform.h>
+
+extern void float_vfp_arm_instruction_test(void);
+extern void float_vfp_thumb_instruction_test(void);
+extern void float_neon_arm_instruction_test(void);
+extern void float_neon_thumb_instruction_test(void);
+
+/* optimize this function to cause it to try to use a lot of registers */
+__OPTIMIZE("O3")
+static int float_thread(void *arg)
+{
+ double *val = arg;
+ uint i, j;
+
+ double a[16];
+
+ /* do a bunch of work with floating point to test context switching */
+ a[0] = *val;
+ for (i = 1; i < countof(a); i++) {
+ a[i] = a[i-1] * 1.01;
+ }
+
+ for (i = 0; i < 1000000; i++) {
+ a[0] += i;
+ for (j = 1; j < countof(a); j++) {
+ a[j] += a[j-1] * 0.00001;
+ }
+ }
+
+ *val = a[countof(a) - 1];
+
+ return 1;
+}
+
+#if ARCH_ARM
+static void arm_float_instruction_trap_test(void)
+{
+ printf("testing fpu trap\n");
+
+#if !ARM_ONLY_THUMB
+ float_vfp_arm_instruction_test();
+ float_neon_arm_instruction_test();
+#endif
+ float_vfp_thumb_instruction_test();
+ float_neon_thumb_instruction_test();
+
+ printf("if we got here, we probably decoded everything properly\n");
+}
+#endif
+
+static void float_tests(void)
+{
+ printf("floating point test:\n");
+
+ /* test lazy fpu load on separate thread */
+ thread_t *t[8];
+ double val[countof(t)];
+
+ printf("creating %u floating point threads\n", countof(t));
+ for (uint i = 0; i < countof(t); i++) {
+ val[i] = i;
+ t[i] = thread_create("float", &float_thread, &val[i], LOW_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_resume(t[i]);
+ }
+
+ int res;
+ for (uint i = 0; i < countof(t); i++) {
+ thread_join(t[i], &res, INFINITE_TIME);
+ printf("float thread %u returns %d, val %f\n", i, res, val[i]);
+ }
+ printf("the above values should be close\n");
+
+#if ARCH_ARM
+ /* test all the instruction traps */
+ arm_float_instruction_trap_test();
+#endif
+}
+
+STATIC_COMMAND_START
+STATIC_COMMAND("float_tests", "floating point test", (console_cmd)&float_tests)
+STATIC_COMMAND_END(float_tests);
+
+#endif // ARM_WITH_VFP || ARCH_ARM64
diff --git a/src/bsp/lk/app/tests/float_instructions.S b/src/bsp/lk/app/tests/float_instructions.S
new file mode 100644
index 0000000..297fcbd
--- /dev/null
+++ b/src/bsp/lk/app/tests/float_instructions.S
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#include <asm.h>
+
+#if ARM_WITH_VFP
+
+.fpu neon
+.syntax unified
+
+.macro disable, scratchreg
+ vmrs \scratchreg, fpexc
+ bic \scratchreg, #(1<<30)
+ vmsr fpexc, \scratchreg
+.endm
+
+.macro vfp_instructions
+ disable r12
+ vadd.f32 s0, s0, s0
+
+ disable r12
+ vadd.f64 d0, d0, d0
+
+ disable r12
+ ldr r0, =float_test_scratch
+ vldr s0, [r0]
+.endm
+
+.macro neon_instructions
+ disable r12
+ vadd.f32 q0, q0, q0
+
+ disable r12
+ ldr r0, =float_test_scratch
+ vld1.f32 { q0 }, [r0]
+
+ disable r12
+ vmov s0, r0
+.endm
+
+#if !ARM_ONLY_THUMB
+.arm
+
+FUNCTION(float_vfp_arm_instruction_test)
+ vfp_instructions
+ bx lr
+
+FUNCTION(float_neon_arm_instruction_test)
+ neon_instructions
+ bx lr
+#endif
+
+.thumb
+
+FUNCTION(float_vfp_thumb_instruction_test)
+ vfp_instructions
+ bx lr
+
+FUNCTION(float_neon_thumb_instruction_test)
+ neon_instructions
+ bx lr
+
+.data
+LOCAL_DATA(float_test_scratch)
+ .word 0
+ .word 0
+
+#endif // ARM_WITH_VFP
diff --git a/src/bsp/lk/app/tests/float_print_host.c b/src/bsp/lk/app/tests/float_print_host.c
new file mode 100644
index 0000000..1176876
--- /dev/null
+++ b/src/bsp/lk/app/tests/float_print_host.c
@@ -0,0 +1,17 @@
+#include <stdio.h>
+
+#define countof(a) (sizeof(a) / sizeof((a)[0]))
+
+#include "float_test_vec.c"
+
+int main(void)
+{
+ printf("floating point printf tests\n");
+
+ for (size_t i = 0; i < float_test_vec_size; i++) {
+ PRINT_FLOAT;
+ }
+
+ return 0;
+}
+
diff --git a/src/bsp/lk/app/tests/float_test_vec.c b/src/bsp/lk/app/tests/float_test_vec.c
new file mode 100644
index 0000000..d7d77c1
--- /dev/null
+++ b/src/bsp/lk/app/tests/float_test_vec.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * 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.
+ */
+
+#include <stdint.h>
+
+union double_int {
+ double d;
+ uint64_t i;
+};
+
+static const union double_int float_test_vec[] = {
+ { .d = -2.0 },
+ { .d = -1.0 },
+ { .d = -0.5 },
+ { .d = -0.0 },
+ { .d = 0.0 },
+ { .d = 0.01 },
+ { .d = 0.1 },
+ { .d = 0.2 },
+ { .d = 0.25 },
+ { .d = 0.5 },
+ { .d = 0.75 },
+ { .d = 1.0 },
+ { .d = 2.0 },
+ { .d = 3.0 },
+ { .d = 10.0 },
+ { .d = 100.0 },
+ { .d = 123456.0 },
+ { .d = -123456.0 },
+ { .d = 546.5645644531f },
+ { .d = -546.5645644531f },
+ { .d = 0.12345 },
+ { .d = 0.0000012345 },
+ { .d = 0.0000019999 },
+ { .d = 0.0000015 },
+ { .i = 0x4005bf0a8b145649ULL }, // e
+ { .i = 0x400921fb54442d18ULL }, // pi
+ { .i = 0x43f0000000000000ULL }, // 2^64
+ { .i = 0x7fefffffffffffffULL }, // largest normalized
+ { .i = 0x0010000000000000ULL }, // least positive normalized
+ { .i = 0x0000000000000001ULL }, // smallest possible denorm
+ { .i = 0x000fffffffffffffULL }, // largest possible denorm
+ { .i = 0x7ff0000000000001ULL }, // smallest SNAn
+ { .i = 0x7ff7ffffffffffffULL }, // largest SNAn
+ { .i = 0x7ff8000000000000ULL }, // smallest QNAn
+ { .i = 0x7fffffffffffffffULL }, // largest QNAn
+ { .i = 0xfff0000000000000ULL }, // -infinity
+ { .i = 0x7ff0000000000000ULL }, // +infinity
+};
+
+#define countof(a) (sizeof(a) / sizeof((a)[0]))
+static const unsigned int float_test_vec_size = countof(float_test_vec);
+
+#define PRINT_FLOAT \
+ printf("0x%016llx %f %F %a %A\n", \
+ float_test_vec[i], \
+ *(const double *)&float_test_vec[i], \
+ *(const double *)&float_test_vec[i], \
+ *(const double *)&float_test_vec[i], \
+ *(const double *)&float_test_vec[i])
+
diff --git a/src/bsp/lk/app/tests/include/app/tests.h b/src/bsp/lk/app/tests/include/app/tests.h
new file mode 100644
index 0000000..ea65bf8
--- /dev/null
+++ b/src/bsp/lk/app/tests/include/app/tests.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2008-2014 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#ifndef __APP_TESTS_H
+#define __APP_TESTS_H
+
+#include <lib/console.h>
+
+int port_tests(int argc, const cmd_args *argv);
+int fibo(int argc, const cmd_args *argv);
+int spinner(int argc, const cmd_args *argv);
+int thread_tests(int argc, const cmd_args *argv);
+int benchmarks(int argc, const cmd_args *argv);
+int clock_tests(int argc, const cmd_args *argv);
+int printf_tests(int argc, const cmd_args *argv);
+int printf_tests_float(int argc, const cmd_args *argv);
+
+#endif
+
diff --git a/src/bsp/lk/app/tests/mem_tests.c b/src/bsp/lk/app/tests/mem_tests.c
new file mode 100644
index 0000000..0b76e64
--- /dev/null
+++ b/src/bsp/lk/app/tests/mem_tests.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <arch.h>
+#include <arch/ops.h>
+#include <lib/console.h>
+#include <platform.h>
+#include <debug.h>
+
+#if WITH_KERNEL_VM
+#include <kernel/vm.h>
+#endif
+
+static void mem_test_fail(void *ptr, uint32_t should, uint32_t is)
+{
+ printf("ERROR at %p: should be 0x%x, is 0x%x\n", ptr, should, is);
+
+ ptr = (void *)ROUNDDOWN((uintptr_t)ptr, 64);
+ hexdump(ptr, 128);
+}
+
+static status_t do_pattern_test(void *ptr, size_t len, uint32_t pat)
+{
+ volatile uint32_t *vbuf32 = ptr;
+ size_t i;
+
+ printf("\tpattern 0x%08x\n", pat);
+ for (i = 0; i < len / 4; i++) {
+ vbuf32[i] = pat;
+ }
+
+ for (i = 0; i < len / 4; i++) {
+ if (vbuf32[i] != pat) {
+ mem_test_fail((void *)&vbuf32[i], pat, vbuf32[i]);
+ return ERR_GENERIC;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+static status_t do_moving_inversion_test(void *ptr, size_t len, uint32_t pat)
+{
+ volatile uint32_t *vbuf32 = ptr;
+ size_t i;
+
+ printf("\tpattern 0x%08x\n", pat);
+
+ /* fill memory */
+ for (i = 0; i < len / 4; i++) {
+ vbuf32[i] = pat;
+ }
+
+ /* from the bottom, walk through each cell, inverting the value */
+ //printf("\t\tbottom up invert\n");
+ for (i = 0; i < len / 4; i++) {
+ if (vbuf32[i] != pat) {
+ mem_test_fail((void *)&vbuf32[i], pat, vbuf32[i]);
+ return ERR_GENERIC;
+ }
+
+ vbuf32[i] = ~pat;
+ }
+
+ /* repeat, walking from top down */
+ //printf("\t\ttop down invert\n");
+ for (i = len / 4; i > 0; i--) {
+ if (vbuf32[i-1] != ~pat) {
+ mem_test_fail((void *)&vbuf32[i-1], ~pat, vbuf32[i-1]);
+ return ERR_GENERIC;
+ }
+
+ vbuf32[i-1] = pat;
+ }
+
+ /* verify that we have the original pattern */
+ //printf("\t\tfinal test\n");
+ for (i = 0; i < len / 4; i++) {
+ if (vbuf32[i] != pat) {
+ mem_test_fail((void *)&vbuf32[i], pat, vbuf32[i]);
+ return ERR_GENERIC;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+static void do_mem_tests(void *ptr, size_t len)
+{
+ size_t i;
+
+ /* test 1: simple write address to memory, read back */
+ printf("test 1: simple address write, read back\n");
+ volatile uint32_t *vbuf32 = ptr;
+ for (i = 0; i < len / 4; i++) {
+ vbuf32[i] = i;
+ }
+
+ for (i = 0; i < len / 4; i++) {
+ if (vbuf32[i] != i) {
+ mem_test_fail((void *)&vbuf32[i], i, vbuf32[i]);
+ goto out;
+ }
+ }
+
+ /* test 2: write various patterns, read back */
+ printf("test 2: write patterns, read back\n");
+
+ static const uint32_t pat[] = {
+ 0x0, 0xffffffff,
+ 0xaaaaaaaa, 0x55555555,
+ };
+
+ for (size_t p = 0; p < countof(pat); p++) {
+ if (do_pattern_test(ptr, len, pat[p]) < 0)
+ goto out;
+ }
+ // shift bits through 32bit word
+ for (uint32_t p = 1; p != 0; p <<= 1) {
+ if (do_pattern_test(ptr, len, p) < 0)
+ goto out;
+ }
+ // shift bits through 16bit word, invert top of 32bit
+ for (uint16_t p = 1; p != 0; p <<= 1) {
+ if (do_pattern_test(ptr, len, ((~p) << 16) | p) < 0)
+ goto out;
+ }
+
+ /* test 3: moving inversion, patterns */
+ printf("test 3: moving inversions with patterns\n");
+ for (size_t p = 0; p < countof(pat); p++) {
+ if (do_moving_inversion_test(ptr, len, pat[p]) < 0)
+ goto out;
+
+ }
+ // shift bits through 32bit word
+ for (uint32_t p = 1; p != 0; p <<= 1) {
+ if (do_moving_inversion_test(ptr, len, p) < 0)
+ goto out;
+ }
+ // shift bits through 16bit word, invert top of 32bit
+ for (uint16_t p = 1; p != 0; p <<= 1) {
+ if (do_moving_inversion_test(ptr, len, ((~p) << 16) | p) < 0)
+ goto out;
+ }
+
+out:
+ printf("done with tests\n");
+}
+
+static int mem_test(int argc, const cmd_args *argv)
+{
+ if (argc < 2) {
+ printf("not enough arguments\n");
+usage:
+ printf("usage: %s <length>\n", argv[0].str);
+ printf("usage: %s <base> <length>\n", argv[0].str);
+ return -1;
+ }
+
+ if (argc == 2) {
+ void *ptr;
+ size_t len = argv[1].u;
+
+#if WITH_KERNEL_VM
+ /* rounding up len to the next page */
+ len = PAGE_ALIGN(len);
+ if (len == 0) {
+ printf("invalid length\n");
+ return -1;
+ }
+
+ /* allocate a region to test in */
+ status_t err = vmm_alloc_contiguous(vmm_get_kernel_aspace(), "memtest", len, &ptr, 0, 0, ARCH_MMU_FLAG_UNCACHED);
+ if (err < 0) {
+ printf("error %d allocating test region\n", err);
+ return -1;
+ }
+
+ paddr_t pa;
+ arch_mmu_query((vaddr_t)ptr, &pa, 0);
+ printf("physical address 0x%lx\n", pa);
+#else
+ /* allocate from the heap */
+ ptr = malloc(len);
+ if (!ptr ) {
+ printf("error allocating test area from heap\n");
+ return -1;
+ }
+
+#endif
+
+ printf("got buffer at %p of length 0x%lx\n", ptr, len);
+
+ /* run the tests */
+ do_mem_tests(ptr, len);
+
+#if WITH_KERNEL_VM
+ // XXX free memory region here
+ printf("NOTE: leaked memory\n");
+#else
+ free(ptr);
+#endif
+ } else if (argc == 3) {
+ void *ptr = argv[1].p;
+ size_t len = argv[2].u;
+
+ /* run the tests */
+ do_mem_tests(ptr, len);
+ } else {
+ goto usage;
+ }
+
+ return 0;
+}
+
+STATIC_COMMAND_START
+STATIC_COMMAND("mem_test", "test memory", &mem_test)
+STATIC_COMMAND_END(mem_tests);
diff --git a/src/bsp/lk/app/tests/port_tests.c b/src/bsp/lk/app/tests/port_tests.c
new file mode 100644
index 0000000..591096c
--- /dev/null
+++ b/src/bsp/lk/app/tests/port_tests.c
@@ -0,0 +1,605 @@
+/*
+ * Copyright (c) 2015 Carlos Pizano-Uribe cpu@chromium.org
+ *
+ * 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.
+ */
+
+#include <debug.h>
+#include <err.h>
+#include <rand.h>
+#include <string.h>
+#include <trace.h>
+
+#include <kernel/port.h>
+#include <kernel/thread.h>
+
+#include <platform.h>
+
+#define LOCAL_TRACE 0
+
+void* context1 = (void*) 0x53;
+
+static void dump_port_result(const port_result_t* result)
+{
+ const port_packet_t* p = &result->packet;
+ LTRACEF("[%02x %02x %02x %02x %02x %02x %02x %02x]\n",
+ p->value[0], p->value[1], p->value[2], p->value[3],
+ p->value[4], p->value[5], p->value[6], p->value[7]);
+}
+
+static int single_thread_basic(void)
+{
+ port_t w_port;
+ status_t st = port_create("sh_prt1", PORT_MODE_UNICAST, &w_port);
+ if (st < 0) {
+ printf("could not create port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ port_t r_port;
+ st = port_open("sh_prt0", context1, &r_port);
+ if (st != ERR_NOT_FOUND) {
+ printf("expected not to find port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ st = port_open("sh_prt1", context1, &r_port);
+ if (st < 0) {
+ printf("could not open port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ port_packet_t packet[3] = {
+ {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}},
+ {{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11}},
+ {{0x33, 0x66, 0x99, 0xcc, 0x33, 0x66, 0x99, 0xcc}},
+ };
+
+ st = port_write(w_port, &packet[0], 1);
+ if (st < 0) {
+ printf("could not write port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ printf("reading from port:\n");
+
+ port_result_t res = {0};
+
+ st = port_read(r_port, 0, &res);
+ if (st < 0) {
+ printf("could not read port, status = %d\n", st);
+ return __LINE__;
+ }
+ if (res.ctx != context1) {
+ printf("bad context! = %p\n", res.ctx);
+ return __LINE__;
+ }
+
+ st = port_read(r_port, 0, &res);
+ if (st != ERR_TIMED_OUT) {
+ printf("expected timeout, status = %d\n", st);
+ return __LINE__;
+ }
+
+ st = port_write(w_port, &packet[1], 1);
+ if (st < 0) {
+ printf("could not write port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ st = port_write(w_port, &packet[0], 1);
+ if (st < 0) {
+ printf("could not write port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ st = port_write(w_port, &packet[2], 1);
+ if (st < 0) {
+ printf("could not write port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ int expected_count = 3;
+ while (true) {
+ st = port_read(r_port, 0, &res);
+ if (st < 0)
+ break;
+ dump_port_result(&res);
+ --expected_count;
+ }
+
+ if (expected_count != 0) {
+ printf("invalid read count = %d\n", expected_count);
+ return __LINE__;
+ }
+
+ printf("\n");
+
+ // port should be empty. should be able to write 8 packets.
+ expected_count = 8;
+ while (true) {
+ st = port_write(w_port, &packet[1], 1);
+ if (st < 0)
+ break;
+ --expected_count;
+ st = port_write(w_port, &packet[2], 1);
+ if (st < 0)
+ break;
+ --expected_count;
+ }
+
+ if (expected_count != 0) {
+ printf("invalid write count = %d\n", expected_count);
+ return __LINE__;
+ }
+
+ // tod(cpu) fix this possibly wrong error.
+ if (st != ERR_PARTIAL_WRITE) {
+ printf("expected buffer error, status =%d\n", st);
+ return __LINE__;
+ }
+
+ // read 3 packets.
+ for (int ix = 0; ix != 3; ++ix) {
+ st = port_read(r_port, 0, &res);
+ if (st < 0) {
+ printf("could not read port, status = %d\n", st);
+ return __LINE__;
+ }
+ }
+
+ // there are 5 packets, now we add another 3.
+ st = port_write(w_port, packet, 3);
+ if (st < 0) {
+ printf("could not write port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ expected_count = 8;
+ while (true) {
+ st = port_read(r_port, 0, &res);
+ if (st < 0)
+ break;
+ dump_port_result(&res);
+ --expected_count;
+ }
+
+ if (expected_count != 0) {
+ printf("invalid read count = %d\n", expected_count);
+ return __LINE__;
+ }
+
+ // attempt to use the wrong port.
+ st = port_write(r_port, &packet[1], 1);
+ if (st != ERR_BAD_HANDLE) {
+ printf("expected bad handle error, status = %d\n", st);
+ return __LINE__;
+ }
+
+ st = port_read(w_port, 0, &res);
+ if (st != ERR_BAD_HANDLE) {
+ printf("expected bad handle error, status = %d\n", st);
+ return __LINE__;
+ }
+
+ st = port_close(r_port);
+ if (st < 0) {
+ printf("could not close read port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ st = port_close(w_port);
+ if (st < 0) {
+ printf("could not close write port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ st = port_close(r_port);
+ if (st != ERR_BAD_HANDLE) {
+ printf("expected bad handle error, status = %d\n", st);
+ return __LINE__;
+ }
+
+ st = port_close(w_port);
+ if (st != ERR_BAD_HANDLE) {
+ printf("expected bad handle error, status = %d\n", st);
+ return __LINE__;
+ }
+
+ st = port_destroy(w_port);
+ if (st < 0) {
+ printf("could not destroy port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ printf("single_thread_basic : ok\n");
+ return 0;
+}
+
+static int ping_pong_thread(void *arg)
+{
+ port_t r_port;
+ status_t st = port_open("ping_port", NULL, &r_port);
+ if (st < 0) {
+ printf("thread: could not open port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ bool should_dispose_pong_port = true;
+ port_t w_port;
+ st = port_create("pong_port", PORT_MODE_UNICAST, &w_port);
+ if (st == ERR_ALREADY_EXISTS) {
+ // won the race to create the port.
+ should_dispose_pong_port = false;
+ } else if (st < 0) {
+ printf("thread: could not open port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ port_result_t pr;
+
+ // the loop is read-mutate-write until the write port
+ // is closed by the master thread.
+ while (true) {
+ st = port_read(r_port, INFINITE_TIME, &pr);
+
+ if (st == ERR_CANCELLED) {
+ break;
+ } else if (st < 0) {
+ printf("thread: could not read port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ pr.packet.value[0]++;
+ pr.packet.value[5]--;
+
+ st = port_write(w_port, &pr.packet, 1);
+ if (st < 0) {
+ printf("thread: could not write port, status = %d\n", st);
+ return __LINE__;
+ }
+ }
+
+ port_close(r_port);
+
+ if (should_dispose_pong_port) {
+ port_close(w_port);
+ port_destroy(w_port);
+ }
+
+ return 0;
+
+bail:
+ return __LINE__;
+}
+
+
+int two_threads_basic(void)
+{
+ port_t w_port;
+ status_t st = port_create("ping_port", PORT_MODE_BROADCAST, &w_port);
+ if (st < 0) {
+ printf("could not create port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ thread_t* t1 = thread_create(
+ "worker1", &ping_pong_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_t* t2 = thread_create(
+ "worker2", &ping_pong_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_resume(t1);
+ thread_resume(t2);
+
+ // wait for the pong port to be created, the two threads race to do it.
+ port_t r_port;
+ while (true) {
+ status_t st = port_open("pong_port", NULL, &r_port);
+ if (st == NO_ERROR) {
+ break;
+ } else if (st == ERR_NOT_FOUND) {
+ thread_sleep(100);
+ } else {
+ printf("could not open port, status = %d\n", st);
+ return __LINE__;
+ }
+ }
+
+ // We have two threads listening to the ping port. Which both reply
+ // on the pong port, so we get two packets in per packet out.
+ const int passes = 256;
+ printf("two_threads_basic test, %d passes\n", passes);
+
+ port_packet_t packet_out = {{0xaf, 0x77, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05}};
+
+ port_result_t pr;
+ for (int ix = 0; ix != passes; ++ix) {
+ const size_t count = 1 + ((unsigned int)rand() % 3);
+
+ for (size_t jx = 0; jx != count; ++jx) {
+ st = port_write(w_port, &packet_out, 1);
+ if (st < 0) {
+ printf("could not write port, status = %d\n", st);
+ return __LINE__;
+ }
+ }
+
+ packet_out.value[0]++;
+ packet_out.value[5]--;
+
+ for (size_t jx = 0; jx != count * 2; ++jx) {
+ st = port_read(r_port, INFINITE_TIME, &pr);
+ if (st < 0) {
+ printf("could not read port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ if ((pr.packet.value[0] != packet_out.value[0]) ||
+ (pr.packet.value[5] != packet_out.value[5])) {
+ printf("unexpected data in packet, loop %d", ix);
+ return __LINE__;
+ }
+ }
+ }
+
+ thread_sleep(100);
+
+ // there should be no more packets to read.
+ st = port_read(r_port, 0, &pr);
+ if (st != ERR_TIMED_OUT) {
+ printf("unexpected packet, status = %d\n", st);
+ return __LINE__;
+ }
+
+ printf("two_threads_basic master shutdown\n");
+
+ st = port_close(r_port);
+ if (st < 0) {
+ printf("could not close port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ st = port_close(w_port);
+ if (st < 0) {
+ printf("could not close port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ st = port_destroy(w_port);
+ if (st < 0) {
+ printf("could not destroy port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ int retcode = -1;
+ thread_join(t1, &retcode, INFINITE_TIME);
+ if (retcode)
+ goto fail;
+
+ thread_join(t2, &retcode, INFINITE_TIME);
+ if (retcode)
+ goto fail;
+
+ return 0;
+
+fail:
+ printf("child thread exited with %d\n", retcode);
+ return __LINE__;
+}
+
+#define CMD_PORT_CTX ((void*) 0x77)
+#define TS1_PORT_CTX ((void*) 0x11)
+#define TS2_PORT_CTX ((void*) 0x12)
+
+typedef enum {
+ ADD_PORT,
+ QUIT
+} action_t;
+
+typedef struct {
+ action_t what;
+ port_t port;
+} watcher_cmd;
+
+status_t send_watcher_cmd(port_t cmd_port, action_t action, port_t port)
+{
+ watcher_cmd cmd = {action, port};
+ return port_write(cmd_port, ((port_packet_t*) &cmd), 1);;
+}
+
+static int group_watcher_thread(void *arg)
+{
+ port_t watched[8] = {0};
+ status_t st = port_open("grp_ctrl", CMD_PORT_CTX, &watched[0]);
+ if (st < 0) {
+ printf("could not open port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ size_t count = 1;
+ port_t group;
+ int ctx_count = -1;
+
+ while (true) {
+ st = port_group(watched, count, &group);
+ if (st < 0) {
+ printf("could not make group, status = %d\n", st);
+ return __LINE__;
+ }
+
+ port_result_t pr;
+ while (true) {
+ st = port_read(group, INFINITE_TIME, &pr);
+ if (st < 0) {
+ printf("could not read port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ if (pr.ctx == CMD_PORT_CTX) {
+ break;
+ } else if (pr.ctx == TS1_PORT_CTX) {
+ ctx_count += 1;
+ } else if (pr.ctx == TS2_PORT_CTX) {
+ ctx_count += 2;
+ } else {
+ printf("unknown context %p\n", pr.ctx);
+ return __LINE__;
+ }
+ }
+
+ // Either adding a port or exiting; either way close the
+ // existing group port and create a new one if needed
+ // at the top of the loop.
+
+ port_close(group);
+ watcher_cmd* wc = (watcher_cmd*) &pr.packet;
+
+ if (wc->what == ADD_PORT) {
+ watched[count++] = wc->port;
+ } else if (wc->what == QUIT) {
+ break;
+ } else {
+ printf("unknown command %d\n", wc->what);
+ return __LINE__;
+ }
+ }
+
+ if (ctx_count != 2) {
+ printf("unexpected context count %d", ctx_count);
+ return __LINE__;
+ }
+
+ printf("group watcher shutdown\n");
+
+ for (size_t ix = 0; ix != count; ++ix) {
+ st = port_close(watched[ix]);
+ if (st < 0) {
+ printf("failed to close read port, status = %d\n", st);
+ return __LINE__;
+ }
+ }
+
+ return 0;
+}
+
+static status_t make_port_pair(const char* name, void* ctx, port_t* write, port_t* read)
+{
+ status_t st = port_create(name, PORT_MODE_UNICAST, write);
+ if (st < 0)
+ return st;
+ return port_open(name,ctx, read);
+}
+
+int group_basic(void)
+{
+ // we spin a thread that connects to a well known port, then we
+ // send two ports that it will add to a group port.
+ port_t cmd_port;
+ status_t st = port_create("grp_ctrl", PORT_MODE_UNICAST, &cmd_port);
+ if (st < 0 ) {
+ printf("could not create port, status = %d\n", st);
+ return __LINE__;
+ }
+
+ thread_t* wt = thread_create(
+ "g_watcher", &group_watcher_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_resume(wt);
+
+ port_t w_test_port1, r_test_port1;
+ st = make_port_pair("tst_port1", TS1_PORT_CTX, &w_test_port1, &r_test_port1);
+ if (st < 0)
+ return __LINE__;
+
+ port_t w_test_port2, r_test_port2;
+ st = make_port_pair("tst_port2", TS2_PORT_CTX, &w_test_port2, &r_test_port2);
+ if (st < 0)
+ return __LINE__;
+
+ st = send_watcher_cmd(cmd_port, ADD_PORT, r_test_port1);
+ if (st < 0)
+ return __LINE__;
+
+ st = send_watcher_cmd(cmd_port, ADD_PORT, r_test_port2);
+ if (st < 0)
+ return __LINE__;
+
+ thread_sleep(50);
+
+ port_packet_t pp = {{0}};
+ st = port_write(w_test_port1, &pp, 1);
+ if (st < 0)
+ return __LINE__;
+
+ st = port_write(w_test_port2, &pp, 1);
+ if (st < 0)
+ return __LINE__;
+
+ st = send_watcher_cmd(cmd_port, QUIT, 0);
+ if (st < 0)
+ return __LINE__;
+
+ int retcode = -1;
+ thread_join(wt, &retcode, INFINITE_TIME);
+ if (retcode) {
+ printf("child thread exited with %d\n", retcode);
+ return __LINE__;
+ }
+
+ st = port_close(w_test_port1);
+ if (st < 0)
+ return __LINE__;
+ st = port_close(w_test_port2);
+ if (st < 0)
+ return __LINE__;
+ st = port_close(cmd_port);
+ if (st < 0)
+ return __LINE__;
+ st = port_destroy(w_test_port1);
+ if (st < 0)
+ return __LINE__;
+ st = port_destroy(w_test_port2);
+ if (st < 0)
+ return __LINE__;
+ st = port_destroy(cmd_port);
+ if (st < 0)
+ return __LINE__;
+
+ return 0;
+}
+
+#define RUN_TEST(t) result = t(); if (result) goto fail
+
+int port_tests(void)
+{
+ int result;
+ int count = 3;
+ while (count--) {
+ RUN_TEST(single_thread_basic);
+ RUN_TEST(two_threads_basic);
+ RUN_TEST(group_basic);
+ }
+
+ printf("all tests passed\n");
+ return 0;
+fail:
+ printf("test failed at line %d\n", result);
+ return 1;
+}
+
+#undef RUN_TEST
diff --git a/src/bsp/lk/app/tests/printf_tests.c b/src/bsp/lk/app/tests/printf_tests.c
new file mode 100644
index 0000000..3a9747f
--- /dev/null
+++ b/src/bsp/lk/app/tests/printf_tests.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2008-2014 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#include <app/tests.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+
+int printf_tests(int argc, const cmd_args *argv)
+{
+ printf("printf tests\n");
+
+ printf("numbers:\n");
+ printf("int8: %hhd %hhd %hhd\n", -12, 0, 254);
+ printf("uint8: %hhu %hhu %hhu\n", -12, 0, 254);
+ printf("int16: %hd %hd %hd\n", -1234, 0, 1234);
+ printf("uint16:%hu %hu %hu\n", -1234, 0, 1234);
+ printf("int: %d %d %d\n", -12345678, 0, 12345678);
+ printf("uint: %u %u %u\n", -12345678, 0, 12345678);
+ printf("long: %ld %ld %ld\n", -12345678L, 0L, 12345678L);
+ printf("ulong: %lu %lu %lu\n", -12345678UL, 0UL, 12345678UL);
+
+ printf("longlong: %lli %lli %lli\n", -12345678LL, 0LL, 12345678LL);
+ printf("ulonglong: %llu %llu %llu\n", -12345678LL, 0LL, 12345678LL);
+ printf("ssize_t: %zd %zd %zd\n", (ssize_t)-12345678, (ssize_t)0, (ssize_t)12345678);
+ printf("usize_t: %zu %zu %zu\n", (size_t)-12345678, (size_t)0, (size_t)12345678);
+ printf("intmax_t: %jd %jd %jd\n", (intmax_t)-12345678, (intmax_t)0, (intmax_t)12345678);
+ printf("uintmax_t: %ju %ju %ju\n", (uintmax_t)-12345678, (uintmax_t)0, (uintmax_t)12345678);
+ printf("ptrdiff_t: %td %td %td\n", (ptrdiff_t)-12345678, (ptrdiff_t)0, (ptrdiff_t)12345678);
+ printf("ptrdiff_t (u): %tu %tu %tu\n", (ptrdiff_t)-12345678, (ptrdiff_t)0, (ptrdiff_t)12345678);
+
+ printf("hex:\n");
+ printf("uint8: %hhx %hhx %hhx\n", -12, 0, 254);
+ printf("uint16:%hx %hx %hx\n", -1234, 0, 1234);
+ printf("uint: %x %x %x\n", -12345678, 0, 12345678);
+ printf("ulong: %lx %lx %lx\n", -12345678UL, 0UL, 12345678UL);
+ printf("ulong: %X %X %X\n", -12345678, 0, 12345678);
+ printf("ulonglong: %llx %llx %llx\n", -12345678LL, 0LL, 12345678LL);
+ printf("usize_t: %zx %zx %zx\n", (size_t)-12345678, (size_t)0, (size_t)12345678);
+
+ printf("alt/sign:\n");
+ printf("uint: %#x %#X\n", 0xabcdef, 0xabcdef);
+ printf("int: %+d %+d\n", 12345678, -12345678);
+ printf("int: % d %+d\n", 12345678, 12345678);
+
+ printf("formatting\n");
+ printf("int: a%8da\n", 12345678);
+ printf("int: a%9da\n", 12345678);
+ printf("int: a%-9da\n", 12345678);
+ printf("int: a%10da\n", 12345678);
+ printf("int: a%-10da\n", 12345678);
+ printf("int: a%09da\n", 12345678);
+ printf("int: a%010da\n", 12345678);
+ printf("int: a%6da\n", 12345678);
+
+ printf("a%1sa\n", "b");
+ printf("a%9sa\n", "b");
+ printf("a%-9sa\n", "b");
+ printf("a%5sa\n", "thisisatest");
+
+ printf("%03d\n", -2); /* '-02' */
+ printf("%0+3d\n", -2); /* '-02' */
+ printf("%0+3d\n", 2); /* '+02' */
+ printf("%+3d\n", 2); /* ' +2' */
+ printf("% 3d\n", -2000); /* '-2000' */
+ printf("% 3d\n", 2000); /* ' 2000' */
+ printf("%+3d\n", 2000); /* '+2000' */
+ printf("%10s\n", "test"); /* ' test' */
+ printf("%010s\n", "test"); /* ' test' */
+ printf("%-10s\n", "test"); /* 'test ' */
+ printf("%-010s\n", "test"); /* 'test ' */
+
+ int err;
+
+ err = printf("a");
+ printf(" returned %d\n", err);
+ err = printf("ab");
+ printf(" returned %d\n", err);
+ err = printf("abc");
+ printf(" returned %d\n", err);
+ err = printf("abcd");
+ printf(" returned %d\n", err);
+ err = printf("abcde");
+ printf(" returned %d\n", err);
+ err = printf("abcdef");
+ printf(" returned %d\n", err);
+
+ /* make sure snprintf terminates at the right spot */
+ char buf[32];
+
+ memset(buf, 0, sizeof(buf));
+ err = sprintf(buf, "0123456789abcdef012345678");
+ printf("sprintf returns %d\n", err);
+ hexdump8(buf, sizeof(buf));
+
+ memset(buf, 0, sizeof(buf));
+ err = snprintf(buf, 15, "0123456789abcdef012345678");
+ printf("snprintf returns %d\n", err);
+ hexdump8(buf, sizeof(buf));
+
+ return NO_ERROR;
+}
+
+#include "float_test_vec.c"
+
+int printf_tests_float(int argc, const cmd_args *argv)
+{
+ printf("floating point printf tests\n");
+
+ for (size_t i = 0; i < float_test_vec_size; i++) {
+ PRINT_FLOAT;
+ }
+
+ return NO_ERROR;
+}
+
+
diff --git a/src/bsp/lk/app/tests/rules.mk b/src/bsp/lk/app/tests/rules.mk
new file mode 100644
index 0000000..aba98c7
--- /dev/null
+++ b/src/bsp/lk/app/tests/rules.mk
@@ -0,0 +1,23 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/benchmarks.c \
+ $(LOCAL_DIR)/cache_tests.c \
+ $(LOCAL_DIR)/clock_tests.c \
+ $(LOCAL_DIR)/fibo.c \
+ $(LOCAL_DIR)/float.c \
+ $(LOCAL_DIR)/float_instructions.S \
+ $(LOCAL_DIR)/float_test_vec.c \
+ $(LOCAL_DIR)/mem_tests.c \
+ $(LOCAL_DIR)/printf_tests.c \
+ $(LOCAL_DIR)/tests.c \
+ $(LOCAL_DIR)/thread_tests.c \
+ $(LOCAL_DIR)/port_tests.c \
+
+MODULE_ARM_OVERRIDE_SRCS := \
+
+MODULE_COMPILEFLAGS += -Wno-format
+
+include make/module.mk
diff --git a/src/bsp/lk/app/tests/tests.c b/src/bsp/lk/app/tests/tests.c
new file mode 100644
index 0000000..4d5dcda
--- /dev/null
+++ b/src/bsp/lk/app/tests/tests.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#include <app.h>
+#include <debug.h>
+#include <app/tests.h>
+#include <compiler.h>
+
+#if defined(WITH_LIB_CONSOLE)
+#include <lib/console.h>
+
+STATIC_COMMAND_START
+STATIC_COMMAND("printf_tests", "test printf", &printf_tests)
+STATIC_COMMAND("printf_tests_float", "test printf with floating point", &printf_tests_float)
+STATIC_COMMAND("thread_tests", "test the scheduler", &thread_tests)
+STATIC_COMMAND("port_tests", "test the ports", &port_tests)
+STATIC_COMMAND("clock_tests", "test clocks", &clock_tests)
+STATIC_COMMAND("bench", "miscellaneous benchmarks", &benchmarks)
+STATIC_COMMAND("fibo", "threaded fibonacci", &fibo)
+STATIC_COMMAND("spinner", "create a spinning thread", &spinner)
+STATIC_COMMAND_END(tests);
+
+#endif
+
+static void tests_init(const struct app_descriptor *app)
+{
+}
+
+APP_START(tests)
+.init = tests_init,
+.flags = 0,
+APP_END
+
diff --git a/src/bsp/lk/app/tests/thread_tests.c b/src/bsp/lk/app/tests/thread_tests.c
new file mode 100644
index 0000000..543bd52
--- /dev/null
+++ b/src/bsp/lk/app/tests/thread_tests.c
@@ -0,0 +1,666 @@
+/*
+ * Copyright (c) 2008-2015 Travis Geiselbrecht
+ *
+ * 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.
+ */
+#include <debug.h>
+#include <trace.h>
+#include <rand.h>
+#include <err.h>
+#include <assert.h>
+#include <string.h>
+#include <app/tests.h>
+#include <kernel/thread.h>
+#include <kernel/mutex.h>
+#include <kernel/semaphore.h>
+#include <kernel/event.h>
+#include <platform.h>
+
+static int sleep_thread(void *arg)
+{
+ for (;;) {
+ printf("sleeper %p\n", get_current_thread());
+ thread_sleep(rand() % 500);
+ }
+ return 0;
+}
+
+int sleep_test(void)
+{
+ int i;
+ for (i=0; i < 16; i++)
+ thread_detach_and_resume(thread_create("sleeper", &sleep_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+ return 0;
+}
+
+static semaphore_t sem;
+static const int sem_total_its = 10000;
+static const int sem_thread_max_its = 1000;
+static const int sem_start_value = 10;
+static int sem_remaining_its = 0;
+static int sem_threads = 0;
+static mutex_t sem_test_mutex;
+
+static int semaphore_producer(void *unused)
+{
+ printf("semaphore producer %p starting up, running for %d iterations\n", get_current_thread(), sem_total_its);
+
+ for (int x = 0; x < sem_total_its; x++) {
+ sem_post(&sem, true);
+ }
+
+ return 0;
+}
+
+static int semaphore_consumer(void *unused)
+{
+ unsigned int iterations = 0;
+
+ mutex_acquire(&sem_test_mutex);
+ if (sem_remaining_its >= sem_thread_max_its) {
+ iterations = rand();
+ iterations %= sem_thread_max_its;
+ } else {
+ iterations = sem_remaining_its;
+ }
+ sem_remaining_its -= iterations;
+ mutex_release(&sem_test_mutex);
+
+ printf("semaphore consumer %p starting up, running for %u iterations\n", get_current_thread(), iterations);
+ for (unsigned int x = 0; x < iterations; x++)
+ sem_wait(&sem);
+ printf("semaphore consumer %p done\n", get_current_thread());
+ atomic_add(&sem_threads, -1);
+ return 0;
+}
+
+static int semaphore_test(void)
+{
+ static semaphore_t isem = SEMAPHORE_INITIAL_VALUE(isem, 99);
+ printf("preinitialized sempahore:\n");
+ hexdump(&isem, sizeof(isem));
+
+ sem_init(&sem, sem_start_value);
+ mutex_init(&sem_test_mutex);
+
+ sem_remaining_its = sem_total_its;
+ while (1) {
+ mutex_acquire(&sem_test_mutex);
+ if (sem_remaining_its) {
+ thread_detach_and_resume(thread_create("semaphore consumer", &semaphore_consumer, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+ atomic_add(&sem_threads, 1);
+ } else {
+ mutex_release(&sem_test_mutex);
+ break;
+ }
+ mutex_release(&sem_test_mutex);
+ }
+
+ thread_detach_and_resume(thread_create("semaphore producer", &semaphore_producer, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+
+ while (sem_threads)
+ thread_yield();
+
+ if (sem.count == sem_start_value)
+ printf("semaphore tests successfully complete\n");
+ else
+ printf("semaphore tests failed: %d != %d\n", sem.count, sem_start_value);
+
+ sem_destroy(&sem);
+ mutex_destroy(&sem_test_mutex);
+
+ return 0;
+}
+
+static int mutex_thread(void *arg)
+{
+ int i;
+ const int iterations = 1000000;
+
+ static volatile int shared = 0;
+
+ mutex_t *m = (mutex_t *)arg;
+
+ printf("mutex tester thread %p starting up, will go for %d iterations\n", get_current_thread(), iterations);
+
+ for (i = 0; i < iterations; i++) {
+ mutex_acquire(m);
+
+ if (shared != 0)
+ panic("someone else has messed with the shared data\n");
+
+ shared = (intptr_t)get_current_thread();
+ thread_yield();
+ shared = 0;
+
+ mutex_release(m);
+ thread_yield();
+ }
+
+ return 0;
+}
+
+static int mutex_timeout_thread(void *arg)
+{
+ mutex_t *timeout_mutex = (mutex_t *)arg;
+ status_t err;
+
+ printf("mutex_timeout_thread acquiring mutex %p with 1 second timeout\n", timeout_mutex);
+ err = mutex_acquire_timeout(timeout_mutex, 1000);
+ if (err == ERR_TIMED_OUT)
+ printf("mutex_acquire_timeout returns with TIMEOUT\n");
+ else
+ printf("mutex_acquire_timeout returns %d\n", err);
+
+ return err;
+}
+
+static int mutex_zerotimeout_thread(void *arg)
+{
+ mutex_t *timeout_mutex = (mutex_t *)arg;
+ status_t err;
+
+ printf("mutex_zerotimeout_thread acquiring mutex %p with zero second timeout\n", timeout_mutex);
+ err = mutex_acquire_timeout(timeout_mutex, 0);
+ if (err == ERR_TIMED_OUT)
+ printf("mutex_acquire_timeout returns with TIMEOUT\n");
+ else
+ printf("mutex_acquire_timeout returns %d\n", err);
+
+ return err;
+}
+
+int mutex_test(void)
+{
+ static mutex_t imutex = MUTEX_INITIAL_VALUE(imutex);
+ printf("preinitialized mutex:\n");
+ hexdump(&imutex, sizeof(imutex));
+
+ mutex_t m;
+ mutex_init(&m);
+
+ thread_t *threads[5];
+
+ for (uint i=0; i < countof(threads); i++) {
+ threads[i] = thread_create("mutex tester", &mutex_thread, &m, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_resume(threads[i]);
+ }
+
+ for (uint i=0; i < countof(threads); i++) {
+ thread_join(threads[i], NULL, INFINITE_TIME);
+ }
+
+ printf("done with simple mutex tests\n");
+
+ printf("testing mutex timeout\n");
+
+ mutex_t timeout_mutex;
+
+ mutex_init(&timeout_mutex);
+ mutex_acquire(&timeout_mutex);
+
+ for (uint i=0; i < 2; i++) {
+ threads[i] = thread_create("mutex timeout tester", &mutex_timeout_thread, (void *)&timeout_mutex, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_resume(threads[i]);
+ }
+
+ for (uint i=2; i < 4; i++) {
+ threads[i] = thread_create("mutex timeout tester", &mutex_zerotimeout_thread, (void *)&timeout_mutex, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_resume(threads[i]);
+ }
+
+ thread_sleep(5000);
+ mutex_release(&timeout_mutex);
+
+ for (uint i=0; i < 4; i++) {
+ thread_join(threads[i], NULL, INFINITE_TIME);
+ }
+
+ printf("done with mutex tests\n");
+
+ mutex_destroy(&timeout_mutex);
+
+ return 0;
+}
+
+static event_t e;
+
+static int event_signaller(void *arg)
+{
+ printf("event signaller pausing\n");
+ thread_sleep(1000);
+
+// for (;;) {
+ printf("signalling event\n");
+ event_signal(&e, true);
+ printf("done signalling event\n");
+ thread_yield();
+// }
+
+ return 0;
+}
+
+static int event_waiter(void *arg)
+{
+ int count = (intptr_t)arg;
+
+ printf("event waiter starting\n");
+
+ while (count > 0) {
+ printf("%p: waiting on event...\n", get_current_thread());
+ if (event_wait(&e) < 0) {
+ printf("%p: event_wait() returned error\n", get_current_thread());
+ return -1;
+ }
+ printf("%p: done waiting on event...\n", get_current_thread());
+ thread_yield();
+ count--;
+ }
+
+ return 0;
+}
+
+void event_test(void)
+{
+ thread_t *threads[5];
+
+ static event_t ievent = EVENT_INITIAL_VALUE(ievent, true, 0x1234);
+ printf("preinitialized event:\n");
+ hexdump(&ievent, sizeof(ievent));
+
+ printf("event tests starting\n");
+
+ /* make sure signalling the event wakes up all the threads */
+ event_init(&e, false, 0);
+ threads[0] = thread_create("event signaller", &event_signaller, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[1] = thread_create("event waiter 0", &event_waiter, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[2] = thread_create("event waiter 1", &event_waiter, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[3] = thread_create("event waiter 2", &event_waiter, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[4] = thread_create("event waiter 3", &event_waiter, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+
+ for (uint i = 0; i < countof(threads); i++)
+ thread_resume(threads[i]);
+
+ thread_sleep(2000);
+ printf("destroying event\n");
+ event_destroy(&e);
+
+ for (uint i = 0; i < countof(threads); i++)
+ thread_join(threads[i], NULL, INFINITE_TIME);
+
+ /* make sure signalling the event wakes up precisely one thread */
+ event_init(&e, false, EVENT_FLAG_AUTOUNSIGNAL);
+ threads[0] = thread_create("event signaller", &event_signaller, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[1] = thread_create("event waiter 0", &event_waiter, (void *)99, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[2] = thread_create("event waiter 1", &event_waiter, (void *)99, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[3] = thread_create("event waiter 2", &event_waiter, (void *)99, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[4] = thread_create("event waiter 3", &event_waiter, (void *)99, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+
+ for (uint i = 0; i < countof(threads); i++)
+ thread_resume(threads[i]);
+
+ thread_sleep(2000);
+ event_destroy(&e);
+
+ for (uint i = 0; i < countof(threads); i++)
+ thread_join(threads[i], NULL, INFINITE_TIME);
+
+ printf("event tests done\n");
+}
+
+static int quantum_tester(void *arg)
+{
+ for (;;) {
+ printf("%p: in this thread. rq %d\n", get_current_thread(), get_current_thread()->remaining_quantum);
+ }
+ return 0;
+}
+
+void quantum_test(void)
+{
+ thread_detach_and_resume(thread_create("quantum tester 0", &quantum_tester, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+ thread_detach_and_resume(thread_create("quantum tester 1", &quantum_tester, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+ thread_detach_and_resume(thread_create("quantum tester 2", &quantum_tester, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+ thread_detach_and_resume(thread_create("quantum tester 3", &quantum_tester, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+}
+
+static event_t context_switch_event;
+static event_t context_switch_done_event;
+
+static int context_switch_tester(void *arg)
+{
+ int i;
+ uint total_count = 0;
+ const int iter = 100000;
+ int thread_count = (intptr_t)arg;
+
+ event_wait(&context_switch_event);
+
+ uint count = arch_cycle_count();
+ for (i = 0; i < iter; i++) {
+ thread_yield();
+ }
+ total_count += arch_cycle_count() - count;
+ thread_sleep(1000);
+ printf("took %u cycles to yield %d times, %u per yield, %u per yield per thread\n",
+ total_count, iter, total_count / iter, total_count / iter / thread_count);
+
+ event_signal(&context_switch_done_event, true);
+
+ return 0;
+}
+
+void context_switch_test(void)
+{
+ event_init(&context_switch_event, false, 0);
+ event_init(&context_switch_done_event, false, 0);
+
+ thread_detach_and_resume(thread_create("context switch idle", &context_switch_tester, (void *)1, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+ thread_sleep(100);
+ event_signal(&context_switch_event, true);
+ event_wait(&context_switch_done_event);
+ thread_sleep(100);
+
+ event_unsignal(&context_switch_event);
+ event_unsignal(&context_switch_done_event);
+ thread_detach_and_resume(thread_create("context switch 2a", &context_switch_tester, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+ thread_detach_and_resume(thread_create("context switch 2b", &context_switch_tester, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+ thread_sleep(100);
+ event_signal(&context_switch_event, true);
+ event_wait(&context_switch_done_event);
+ thread_sleep(100);
+
+ event_unsignal(&context_switch_event);
+ event_unsignal(&context_switch_done_event);
+ thread_detach_and_resume(thread_create("context switch 4a", &context_switch_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+ thread_detach_and_resume(thread_create("context switch 4b", &context_switch_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+ thread_detach_and_resume(thread_create("context switch 4c", &context_switch_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+ thread_detach_and_resume(thread_create("context switch 4d", &context_switch_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+ thread_sleep(100);
+ event_signal(&context_switch_event, true);
+ event_wait(&context_switch_done_event);
+ thread_sleep(100);
+}
+
+static volatile int atomic;
+static volatile int atomic_count;
+
+static int atomic_tester(void *arg)
+{
+ int add = (intptr_t)arg;
+ int i;
+
+ const int iter = 10000000;
+
+ TRACEF("add %d, %d iterations\n", add, iter);
+
+ for (i=0; i < iter; i++) {
+ atomic_add(&atomic, add);
+ }
+
+ int old = atomic_add(&atomic_count, -1);
+ TRACEF("exiting, old count %d\n", old);
+
+ return 0;
+}
+
+static void atomic_test(void)
+{
+ atomic = 0;
+ atomic_count = 8;
+
+ printf("testing atomic routines\n");
+
+ thread_t *threads[8];
+ threads[0] = thread_create("atomic tester 1", &atomic_tester, (void *)1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[1] = thread_create("atomic tester 1", &atomic_tester, (void *)1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[2] = thread_create("atomic tester 1", &atomic_tester, (void *)1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[3] = thread_create("atomic tester 1", &atomic_tester, (void *)1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[4] = thread_create("atomic tester 2", &atomic_tester, (void *)-1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[5] = thread_create("atomic tester 2", &atomic_tester, (void *)-1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[6] = thread_create("atomic tester 2", &atomic_tester, (void *)-1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
+ threads[7] = thread_create("atomic tester 2", &atomic_tester, (void *)-1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
+
+ /* start all the threads */
+ for (uint i = 0; i < countof(threads); i++)
+ thread_resume(threads[i]);
+
+ /* wait for them to all stop */
+ for (uint i = 0; i < countof(threads); i++) {
+ thread_join(threads[i], NULL, INFINITE_TIME);
+ }
+
+ printf("atomic count == %d (should be zero)\n", atomic);
+}
+
+static volatile int preempt_count;
+
+static int preempt_tester(void *arg)
+{
+ spin(1000000);
+
+ printf("exiting ts %lld\n", current_time_hires());
+
+ atomic_add(&preempt_count, -1);
+#undef COUNT
+
+ return 0;
+}
+
+static void preempt_test(void)
+{
+ /* create 5 threads, let them run. If the system is properly timer preempting,
+ * the threads should interleave each other at a fine enough granularity so
+ * that they complete at roughly the same time. */
+ printf("testing preemption\n");
+
+ preempt_count = 5;
+
+ for (int i = 0; i < preempt_count; i++)
+ thread_detach_and_resume(thread_create("preempt tester", &preempt_tester, NULL, LOW_PRIORITY, DEFAULT_STACK_SIZE));
+
+ while (preempt_count > 0) {
+ thread_sleep(1000);
+ }
+
+ printf("done with preempt test, above time stamps should be very close\n");
+
+ /* do the same as above, but mark the threads as real time, which should
+ * effectively disable timer based preemption for them. They should
+ * complete in order, about a second apart. */
+ printf("testing real time preemption\n");
+
+ preempt_count = 5;
+
+ for (int i = 0; i < preempt_count; i++) {
+ thread_t *t = thread_create("preempt tester", &preempt_tester, NULL, LOW_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_set_real_time(t);
+ thread_detach_and_resume(t);
+ }
+
+ while (preempt_count > 0) {
+ thread_sleep(1000);
+ }
+
+ printf("done with real-time preempt test, above time stamps should be 1 second apart\n");
+}
+
+static int join_tester(void *arg)
+{
+ long val = (long)arg;
+
+ printf("\t\tjoin tester starting\n");
+ thread_sleep(500);
+ printf("\t\tjoin tester exiting with result %ld\n", val);
+
+ return val;
+}
+
+static int join_tester_server(void *arg)
+{
+ int ret;
+ status_t err;
+ thread_t *t;
+
+ printf("\ttesting thread_join/thread_detach\n");
+
+ printf("\tcreating and waiting on thread to exit with thread_join\n");
+ t = thread_create("join tester", &join_tester, (void *)1, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_resume(t);
+ ret = 99;
+ printf("\tthread magic is 0x%x (should be 0x%x)\n", t->magic, THREAD_MAGIC);
+ err = thread_join(t, &ret, INFINITE_TIME);
+ printf("\tthread_join returns err %d, retval %d\n", err, ret);
+ printf("\tthread magic is 0x%x (should be 0)\n", t->magic);
+
+ printf("\tcreating and waiting on thread to exit with thread_join, after thread has exited\n");
+ t = thread_create("join tester", &join_tester, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_resume(t);
+ thread_sleep(1000); // wait until thread is already dead
+ ret = 99;
+ printf("\tthread magic is 0x%x (should be 0x%x)\n", t->magic, THREAD_MAGIC);
+ err = thread_join(t, &ret, INFINITE_TIME);
+ printf("\tthread_join returns err %d, retval %d\n", err, ret);
+ printf("\tthread magic is 0x%x (should be 0)\n", t->magic);
+
+ printf("\tcreating a thread, detaching it, let it exit on its own\n");
+ t = thread_create("join tester", &join_tester, (void *)3, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_detach(t);
+ thread_resume(t);
+ thread_sleep(1000); // wait until the thread should be dead
+ printf("\tthread magic is 0x%x (should be 0)\n", t->magic);
+
+ printf("\tcreating a thread, detaching it after it should be dead\n");
+ t = thread_create("join tester", &join_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_resume(t);
+ thread_sleep(1000); // wait until thread is already dead
+ printf("\tthread magic is 0x%x (should be 0x%x)\n", t->magic, THREAD_MAGIC);
+ thread_detach(t);
+ printf("\tthread magic is 0x%x\n", t->magic);
+
+ printf("\texiting join tester server\n");
+
+ return 55;
+}
+
+static void join_test(void)
+{
+ int ret;
+ status_t err;
+ thread_t *t;
+
+ printf("testing thread_join/thread_detach\n");
+
+ printf("creating thread join server thread\n");
+ t = thread_create("join tester server", &join_tester_server, (void *)1, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_resume(t);
+ ret = 99;
+ err = thread_join(t, &ret, INFINITE_TIME);
+ printf("thread_join returns err %d, retval %d (should be 0 and 55)\n", err, ret);
+}
+
+static void spinlock_test(void)
+{
+ spin_lock_saved_state_t state;
+ spin_lock_t lock;
+
+ spin_lock_init(&lock);
+
+ // verify basic functionality (single core)
+ printf("testing spinlock:\n");
+ ASSERT(!spin_lock_held(&lock));
+ ASSERT(!arch_ints_disabled());
+ spin_lock_irqsave(&lock, state);
+ ASSERT(arch_ints_disabled());
+ ASSERT(spin_lock_held(&lock));
+ spin_unlock_irqrestore(&lock, state);
+ ASSERT(!spin_lock_held(&lock));
+ ASSERT(!arch_ints_disabled());
+ printf("seems to work\n");
+
+#define COUNT (1024*1024)
+ uint32_t c = arch_cycle_count();
+ for (uint i = 0; i < COUNT; i++) {
+ spin_lock(&lock);
+ spin_unlock(&lock);
+ }
+ c = arch_cycle_count() - c;
+
+ printf("%u cycles to acquire/release lock %u times (%u cycles per)\n", c, COUNT, c / COUNT);
+
+ c = arch_cycle_count();
+ for (uint i = 0; i < COUNT; i++) {
+ spin_lock_irqsave(&lock, state);
+ spin_unlock_irqrestore(&lock, state);
+ }
+ c = arch_cycle_count() - c;
+
+ printf("%u cycles to acquire/release lock w/irqsave %u times (%u cycles per)\n", c, COUNT, c / COUNT);
+#undef COUNT
+}
+
+int thread_tests(int argc, const cmd_args *argv)
+{
+ mutex_test();
+ semaphore_test();
+ event_test();
+
+ spinlock_test();
+ atomic_test();
+
+ thread_sleep(200);
+ context_switch_test();
+
+ preempt_test();
+
+ join_test();
+
+ return 0;
+}
+
+static int spinner_thread(void *arg)
+{
+ for (;;)
+ ;
+
+ return 0;
+}
+
+int spinner(int argc, const cmd_args *argv)
+{
+ if (argc < 2) {
+ printf("not enough args\n");
+ printf("usage: %s <priority> <rt>\n", argv[0].str);
+ return -1;
+ }
+
+ thread_t *t = thread_create("spinner", spinner_thread, NULL, argv[1].u, DEFAULT_STACK_SIZE);
+ if (!t)
+ return ERR_NO_MEMORY;
+
+ if (argc >= 3 && !strcmp(argv[2].str, "rt")) {
+ thread_set_real_time(t);
+ }
+ thread_resume(t);
+
+ return 0;
+}
+
+/* vim: set ts=4 sw=4 noexpandtab: */