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