1 /*
2 * Copyright (c) 2013-2015 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #if ARM_WITH_VFP || ARCH_ARM64 || X86_WITH_FPU || (ARCH_RISCV && RISCV_FPU)
9
10 #include <stdio.h>
11 #include <inttypes.h>
12 #include <rand.h>
13 #include <string.h>
14 #include <lk/err.h>
15 #include <lk/console_cmd.h>
16 #include <app/tests.h>
17 #include <kernel/thread.h>
18 #include <kernel/mutex.h>
19 #include <kernel/semaphore.h>
20 #include <kernel/event.h>
21 #include <platform.h>
22
23 extern void float_vfp_arm_instruction_test(void);
24 extern void float_vfp_thumb_instruction_test(void);
25 extern void float_neon_arm_instruction_test(void);
26 extern void float_neon_thumb_instruction_test(void);
27
28 #if ARM_WITH_VFP_SP_ONLY
29 #define FLOAT float
30 #else
31 #define FLOAT double
32 #endif
33
34 /* optimize this function to cause it to try to use a lot of registers */
35 __OPTIMIZE("O3")
float_thread(void * arg)36 static int float_thread(void *arg) {
37 FLOAT *val = arg;
38
39 FLOAT a[16];
40
41 /* do a bunch of work with floating point to test context switching */
42 a[0] = *val;
43 for (size_t i = 1; i < countof(a); i++) {
44 a[i] = a[i-1] * (FLOAT)1.01f;
45 }
46
47 for (size_t i = 0; i < 1000000; i++) {
48 a[0] += (FLOAT)0.001f;
49 for (size_t j = 1; j < countof(a); j++) {
50 a[j] += a[j-1] * (FLOAT)0.00001f;
51 }
52 }
53
54 *val = a[countof(a) - 1];
55
56 return 1;
57 }
58
59 #if ARCH_ARM && !ARM_ISA_ARMV7M
arm_float_instruction_trap_test(void)60 static void arm_float_instruction_trap_test(void) {
61 printf("testing fpu trap\n");
62
63 #if !ARM_ONLY_THUMB
64 float_vfp_arm_instruction_test();
65 float_neon_arm_instruction_test();
66 #endif
67 float_vfp_thumb_instruction_test();
68 float_neon_thumb_instruction_test();
69
70 printf("if we got here, we probably decoded everything properly\n");
71 }
72 #endif
73
float_test(void)74 static void float_test(void) {
75 /* test lazy fpu load on separate thread */
76 thread_t *t[8];
77 volatile FLOAT val[countof(t)];
78 const uint32_t test_results_32[8] = {
79 0x473aced4,
80 0x4788973e,
81 0x47b3c703,
82 0x47def6ab,
83 0x48051399,
84 0x481aaab3,
85 0x48304325,
86 0x4845da0c,
87 };
88 const uint64_t test_results_64[8] = {
89 0x40e7570fc8092db9,
90 0x40f1117b2a41e1dc,
91 0x40f6776e707f2b8a,
92 0x40fbdd61b6bc75cf,
93 0x4100a1aa7e7cdfa2,
94 0x410354a4219b8561,
95 0x4106079dc4ba29ff,
96 0x4108ba9767d8cf09,
97 };
98
99
100 printf("creating %zu floating point threads\n", countof(t));
101 for (uint i = 0; i < countof(t); i++) {
102 val[i] = i;
103 char name[32];
104 snprintf(name, sizeof(name), "float %u", i);
105 t[i] = thread_create(name, &float_thread, (void *)&val[i], LOW_PRIORITY, DEFAULT_STACK_SIZE);
106 thread_resume(t[i]);
107 }
108
109 int res;
110 for (uint i = 0; i < countof(t); i++) {
111 thread_join(t[i], &res, INFINITE_TIME);
112
113 if (sizeof(FLOAT) == 4) {
114 float result = val[i];
115 uint32_t result_u32;
116 memcpy(&result_u32, &result, sizeof(result_u32));
117 printf("float thread %u returns %d, hex val %a, uint32 %#" PRIx32, i, res, (double)result, result_u32);
118 if (result_u32 != test_results_32[i]) {
119 printf("\nfloat thread %u failed, expected %#" PRIx32 "\n", i, test_results_32[i]);
120 } else {
121 printf(" (ok)\n");
122 }
123 } else {
124 double result = val[i];
125 uint64_t result_u64;
126 memcpy(&result_u64, &result, sizeof(result_u64));
127 printf("float thread %u returns %d, hex val %a, uint64 %#" PRIx64, i, res, result, result_u64);
128 if (result_u64 != test_results_64[i]) {
129 printf("\nfloat thread %u failed, expected %#" PRIx64 "\n", i, test_results_64[i]);
130 } else {
131 printf(" (ok)\n");
132 }
133 //hexdump8(&result, 8);
134 }
135 }
136 }
137
float_tests(int argc,const console_cmd_args * argv)138 static int float_tests(int argc, const console_cmd_args *argv) {
139 printf("floating point test:\n");
140
141 float_test();
142
143 #if ARCH_ARM && !ARM_ISA_ARMV7M
144 /* test all the instruction traps */
145 arm_float_instruction_trap_test();
146 #endif
147
148 return 0;
149 }
150
151 STATIC_COMMAND_START
152 STATIC_COMMAND("float_tests", "floating point test", &float_tests)
153 STATIC_COMMAND_END(float_tests);
154
155 #endif // ARM_WITH_VFP || ARCH_ARM64
156