1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #ifdef ZCBOR_VERBOSE
8
9 #include <zephyr/ztest.h>
10
ZTEST(zcbor_unit_tests3,test_skip)11 ZTEST(zcbor_unit_tests3, test_skip)
12 {
13 printf("Skip on VERBOSE builds because of print output volume.\n");
14 }
15
16 #else /* ZCBOR_VERBOSE */
17
18 #include <zephyr/ztest.h>
19 #include "zcbor_decode.h"
20 #include "zcbor_encode.h"
21
22 #include <math.h>
23
24 __attribute__((used))
switch_bytes(uint16_t in)25 static uint16_t switch_bytes(uint16_t in)
26 {
27 return (in >> 8) | (in << 8);
28 }
29
30 /* These are created by
31 * ld -r -b binary -o fp_bytes_decode.o fp_bytes_decode.bin */
32 extern uint8_t _binary_fp_bytes_decode_bin_start[];
33 extern uint8_t _binary_fp_bytes_decode_bin_end[];
34 extern uint8_t _binary_fp_bytes_decode_bin_size[];
35
36 #define F16_MANTISSA_MSK 0x3FF /* Bitmask for the mantissa. */
37
ZTEST(zcbor_unit_tests3,test_float16_decode)38 ZTEST(zcbor_unit_tests3, test_float16_decode)
39 {
40 #ifdef ZCBOR_BIG_ENDIAN
41 float *fps = ((float *)_binary_fp_bytes_decode_bin_start) + 0x10000;
42 #else
43 float *fps = (float *)_binary_fp_bytes_decode_bin_start;
44 #endif
45 uint8_t payload[3] = {0xf9, 0, 0};
46
47 zassert_equal((size_t)&_binary_fp_bytes_decode_bin_size, 0x10000 * sizeof(float) * 2,
48 "0x%x != 0x%x", (size_t)&_binary_fp_bytes_decode_bin_size,
49 0x10000 * sizeof(float));
50
51 for (int i = 0; i <= 0xFFFF; i++) {
52 uint16_t i_be = i;
53 #ifndef ZCBOR_BIG_ENDIAN
54 i_be = switch_bytes(i);
55 #endif
56 *(uint16_t *)&payload[1] = i_be;
57
58 float out;
59 ZCBOR_STATE_D(state_d, 0, payload, sizeof(payload), 1, 0);
60 ZCBOR_STATE_E(state_e, 0, payload, sizeof(payload), 0);
61
62 zassert_true(zcbor_float16_decode(state_d, &out), NULL);
63 zassert_true(zcbor_float16_put(state_e, out));
64
65
66 uint16_t payload_ne = *(uint16_t *)&payload[1];
67 #ifndef ZCBOR_BIG_ENDIAN
68 payload_ne = switch_bytes(payload_ne);
69 #endif
70
71 if (isnan(fps[i])) {
72 zassert_true(isnan(out), "Expected NaN (i = %d)\n", i);
73 /* Mask out mantissa when comparing NaNs. */
74 zassert_equal(payload_ne & ~F16_MANTISSA_MSK, i & ~F16_MANTISSA_MSK,
75 "0x%04x != 0x%04x", payload_ne, i);
76 /* Just check that mantissa is non-zero. */
77 zassert_true(payload_ne & F16_MANTISSA_MSK, NULL);
78 } else {
79 zassert_equal(out, fps[i], "Failed i 0x%x (0x%x != 0x%x)\n",
80 i, *(uint32_t *)&out, *(uint32_t *)&fps[i]);
81 zassert_equal(payload_ne, i,
82 "0x%04x != 0x%04x", payload_ne, i);
83 }
84 }
85 }
86
87
88 /* These are created by
89 * ld -r -b binary -o fp_bytes_encode.o fp_bytes_encode.bin */
90 extern uint8_t _binary_fp_bytes_encode_bin_start[];
91 extern uint8_t _binary_fp_bytes_encode_bin_end[];
92 extern uint8_t _binary_fp_bytes_encode_bin_size[];
93
94 /**
95 * fp_bytes_encode.bin contains results from conversion from float32 to float16.
96 * As an optimization, fp_bytes_encode.bin contains only values for the positive
97 * nonzero_finite part.
98
99 * positive:
100 * zero: 0x00000000 0x33000000
101 * nonzero finite: 0x33000001 0x477fefff
102 * infinite: 0x477ff000 0x7f800000
103 * nan: 0x7f800001 0x7fffffff
104 *
105 * negative:
106 * zero: 0x80000000 0xb3000000
107 * nonzero finite: 0xb3000001 0xc77fefff
108 * infinite: 0xc77ff000 0xff800000
109 * nan: 0xff800001 0xffffffff
110 */
111 #define START_ZERO 0x00000000
112 #define START_NONZERO_FINITE 0x33000001
113 #define START_INFINITE 0x477ff000
114 #define START_NAN 0x7f800001
115
116 /* Return float16 results from fp_bytes_encode.bin */
get_fp_bytes()117 static uint16_t get_fp_bytes()
118 {
119 #ifdef ZCBOR_BIG_ENDIAN
120 uint32_t *fps = ((uint32_t *)_binary_fp_bytes_encode_bin_start) + 31742;
121 #else
122 uint32_t *fps = (uint32_t *)_binary_fp_bytes_encode_bin_start;
123 #endif
124 static uint16_t retval = 0;
125 static uint32_t i = 0;
126
127 if (i++ == fps[retval]) {
128 retval++;
129 }
130
131 return retval + 1;
132 }
133
print_percent(uint32_t i)134 void print_percent(uint32_t i)
135 {
136 if ((i % 0x800000) == 0) {
137 static const uint8_t move_cursor_up[] = {0x1b, 0x5b, 0x41, 0};
138 printf("%s", move_cursor_up);
139 printf("\r%d%%\n", (int)((double)(i) / 0x80000000 * 100));
140 }
141 }
142
143
144 /* Test a single value, and its negative counterpart. */
do_test(uint32_t i,uint16_t exp_value)145 static void do_test(uint32_t i, uint16_t exp_value)
146 {
147 uint32_t i2 = i + 0x80000000; /* Negative i */
148
149 /* Reinterpret as floats. */
150 float in = *(float *)&i;
151 float in2 = *(float *)&i2;
152
153 uint16_t out = zcbor_float32_to_16(in);
154 uint16_t out2 = zcbor_float32_to_16(in2);
155
156 print_percent(i);
157
158 /* Check positive and negative. */
159 zassert_true((out == exp_value) && (out2 == (exp_value + 0x8000)),
160 "Failed i 0x%x (0x%x != 0x%x) or (0x%x != 0x%x)\n", i,
161 out, exp_value, out2, exp_value + 0x8000);
162 }
163
164
do_test_nan(uint32_t i)165 static void do_test_nan(uint32_t i)
166 {
167 float in = *(float *)&i;
168 uint32_t i2 = i + 0x80000000;
169 float in2 = *(float *)&i2;
170
171 print_percent(i);
172
173 uint16_t out = zcbor_float32_to_16(in);
174 uint16_t out2 = zcbor_float32_to_16(in2);
175 float out3 = zcbor_float16_to_32(out);
176 float out4 = zcbor_float16_to_32(out2);
177
178 zassert_true(isnan(out3), "Expected NaN: 0x%x (i = 0x%x)\n", out, i);
179 zassert_true(isnan(out4), "Expected NaN: 0x%x (i = 0x%x)\n", out2, i2);
180 }
181
182
ZTEST(zcbor_unit_tests3,test_float16_encode)183 ZTEST(zcbor_unit_tests3, test_float16_encode)
184 {
185 zassert_equal((size_t)&_binary_fp_bytes_encode_bin_size, 31742 * 4 * 2, NULL);
186 printf("\n");
187
188 for (uint32_t i = START_ZERO; i < START_NONZERO_FINITE; i++) {
189 do_test(i, 0);
190 }
191 for (uint32_t i = START_NONZERO_FINITE; i < START_INFINITE; i++) {
192 do_test(i, get_fp_bytes());
193 }
194 for (uint32_t i = START_INFINITE; i < START_NAN; i++) {
195 do_test(i, 0x7c00);
196 }
197 for (uint32_t i = START_NAN; i < 0x80000000; i++) {
198 do_test_nan(i);
199 }
200 }
201 #endif /* ZCBOR_VERBOSE */
202
203 ZTEST_SUITE(zcbor_unit_tests3, NULL, NULL, NULL, NULL, NULL);
204