| 1 | /* |
| 2 | * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. |
| 3 | * |
| 4 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 5 | */ |
| 6 | |
| 7 | #include <stdio.h> |
| 8 | #include <stdint.h> |
| 9 | #include <stdbool.h> |
| 10 | #include <string.h> |
| 11 | #include <hexagon_types.h> |
| 12 | #include <hvx_hexagon_protos.h> |
| 13 | |
| 14 | int err; |
| 15 | #include "hvx_misc.h" |
| 16 | |
| 17 | #if __HEXAGON_ARCH__ > 75 |
| 18 | #error "After v75, compiler will replace some FP HVX instructions." |
| 19 | #endif |
| 20 | |
| 21 | /****************************************************************************** |
| 22 | * NAN handling |
| 23 | *****************************************************************************/ |
| 24 | |
| 25 | #define isnan(X) \ |
| 26 | (sizeof(X) == bytes_hf ? ((raw_hf(X) & ~0x8000) > 0x7c00) : \ |
| 27 | ((raw_sf(X) & ~(1 << 31)) > 0x7f800000UL)) |
| 28 | |
| 29 | #define CHECK_NAN(A, DEF_NAN) (isnan(A) ? DEF_NAN : (A)) |
| 30 | #define NAN_SF float_sf(0x7FFFFFFF) |
| 31 | #define NAN_HF float_hf(0x7FFF) |
| 32 | #define NAN_BF float_hf(0x7FFF) |
| 33 | |
| 34 | /****************************************************************************** |
| 35 | * Binary operations |
| 36 | *****************************************************************************/ |
| 37 | |
| 38 | #define DEF_TEST_OP_2(vop, op, type_res, type_arg) \ |
| 39 | static void test_##vop##_##type_res##_##type_arg(void) \ |
| 40 | { \ |
| 41 | memset(expect, 0xff, sizeof(expect)); \ |
| 42 | memset(output, 0xff, sizeof(output)); \ |
| 43 | for (int i = 0; i < BUFSIZE; i++) { \ |
| 44 | HVX_Vector *hvx_output = (HVX_Vector *)&output[i]; \ |
| 45 | HVX_Vector hvx_buffer0 = *(HVX_Vector *)&buffer0[i]; \ |
| 46 | HVX_Vector hvx_buffer1 = *(HVX_Vector *)&buffer1[i]; \ |
| 47 | *hvx_output = \ |
| 48 | Q6_V##type_res##_##vop##_V##type_arg##V##type_arg(hvx_buffer0, \ |
| 49 | hvx_buffer1); \ |
| 50 | for (int j = 0; j < MAX_VEC_SIZE_BYTES / bytes_##type_res; j++) { \ |
| 51 | expect[i].type_res[j] = \ |
| 52 | raw_##type_res(op(float_##type_arg(buffer0[i].type_arg[j]), \ |
| 53 | float_##type_arg(buffer1[i].type_arg[j]))); \ |
| 54 | } \ |
| 55 | } \ |
| 56 | check_output_##type_res(__LINE__, BUFSIZE); \ |
| 57 | } |
| 58 | |
| 59 | #define SUM(X, Y, DEF_NAN) CHECK_NAN((X) + (Y), DEF_NAN) |
| 60 | #define SUB(X, Y, DEF_NAN) CHECK_NAN((X) - (Y), DEF_NAN) |
| 61 | #define MULT(X, Y, DEF_NAN) CHECK_NAN((X) * (Y), DEF_NAN) |
| 62 | |
| 63 | #define SUM_SF(X, Y) SUM(X, Y, NAN_SF) |
| 64 | #define SUM_HF(X, Y) SUM(X, Y, NAN_HF) |
| 65 | #define SUB_SF(X, Y) SUB(X, Y, NAN_SF) |
| 66 | #define SUB_HF(X, Y) SUB(X, Y, NAN_HF) |
| 67 | #define MULT_SF(X, Y) MULT(X, Y, NAN_SF) |
| 68 | #define MULT_HF(X, Y) MULT(X, Y, NAN_HF) |
| 69 | |
| 70 | DEF_TEST_OP_2(vadd, SUM_SF, sf, sf); |
| 71 | DEF_TEST_OP_2(vadd, SUM_HF, hf, hf); |
| 72 | DEF_TEST_OP_2(vsub, SUB_SF, sf, sf); |
| 73 | DEF_TEST_OP_2(vsub, SUB_HF, hf, hf); |
| 74 | DEF_TEST_OP_2(vmpy, MULT_SF, sf, sf); |
| 75 | DEF_TEST_OP_2(vmpy, MULT_HF, hf, hf); |
| 76 | |
| 77 | #define signbit_fp(X) \ |
| 78 | (sizeof(X) == bytes_hf ? ((raw_hf(X) & 0x8000) != 0) : \ |
| 79 | ((raw_sf(X) & 0x80000000) != 0)) |
| 80 | |
| 81 | #define STD_MIN(X, Y) ((X) < (Y) ? (X) : (Y)) |
| 82 | #define STD_MAX(X, Y) ((X) > (Y) ? (X) : (Y)) |
| 83 | |
| 84 | #define MIN(X, Y, DEF_NAN) \ |
| 85 | ((isnan(X) || isnan(Y)) ? DEF_NAN : \ |
| 86 | ((X) != (Y)) ? STD_MIN(X, Y) : (signbit_fp(X) ? (X) : (Y))) /* -0 < +0 */ |
| 87 | #define MAX(X, Y, DEF_NAN) \ |
| 88 | ((isnan(X) || isnan(Y)) ? DEF_NAN : \ |
| 89 | ((X) != (Y)) ? STD_MAX(X, Y) : (signbit_fp(X) ? (Y) : (X))) /* -0 < +0 */ |
| 90 | |
| 91 | #define MIN_HF(X, Y) MIN(X, Y, NAN_HF) |
| 92 | #define MAX_HF(X, Y) MAX(X, Y, NAN_HF) |
| 93 | #define MIN_SF(X, Y) MIN(X, Y, NAN_SF) |
| 94 | #define MAX_SF(X, Y) MAX(X, Y, NAN_SF) |
| 95 | #define MIN_BF(X, Y) MIN(X, Y, NAN_BF) |
| 96 | #define MAX_BF(X, Y) MAX(X, Y, NAN_BF) |
| 97 | |
| 98 | DEF_TEST_OP_2(vfmin, MIN_SF, sf, sf); |
| 99 | DEF_TEST_OP_2(vfmax, MAX_SF, sf, sf); |
| 100 | DEF_TEST_OP_2(vfmin, MIN_HF, hf, hf); |
| 101 | DEF_TEST_OP_2(vfmax, MAX_HF, hf, hf); |
| 102 | DEF_TEST_OP_2(vmin, MIN_BF, bf, bf); |
| 103 | DEF_TEST_OP_2(vmax, MAX_BF, bf, bf); |
| 104 | |
| 105 | #define DEF_TEST_OP_2_INTERLEAVED(vop, op, type_res, type_arg) \ |
| 106 | static void test_##vop##_##type_res##_##type_arg(void) \ |
| 107 | { \ |
| 108 | memset(expect, 0xff, sizeof(expect)); \ |
| 109 | memset(output, 0xff, sizeof(output)); \ |
| 110 | for (int i = 0; i < BUFSIZE / 2; i++) { \ |
| 111 | HVX_VectorPair *hvx_output = (HVX_VectorPair *)&output[2 * i]; \ |
| 112 | HVX_Vector hvx_buffer0 = *(HVX_Vector *)&buffer0[i]; \ |
| 113 | HVX_Vector hvx_buffer1 = *(HVX_Vector *)&buffer1[i]; \ |
| 114 | *hvx_output = \ |
| 115 | Q6_W##type_res##_##vop##_V##type_arg##V##type_arg(hvx_buffer0, \ |
| 116 | hvx_buffer1); \ |
| 117 | for (int j = 0; j < MAX_VEC_SIZE_BYTES / bytes_##type_res; j++) { \ |
| 118 | expect[2 * i].type_res[j] = \ |
| 119 | raw_##type_res(op(float_##type_arg(buffer0[i].type_arg[2 * j]), \ |
| 120 | float_##type_arg(buffer1[i].type_arg[2 * j]))); \ |
| 121 | expect[2 * i + 1].type_res[j] = \ |
| 122 | raw_##type_res(op(float_##type_arg(buffer0[i].type_arg[2 * j + 1]), \ |
| 123 | float_##type_arg(buffer1[i].type_arg[2 * j + 1]))); \ |
| 124 | } \ |
| 125 | } \ |
| 126 | check_output_##type_res(__LINE__, BUFSIZE); \ |
| 127 | } |
| 128 | |
| 129 | DEF_TEST_OP_2_INTERLEAVED(vadd, SUM_SF, sf, bf); |
| 130 | DEF_TEST_OP_2_INTERLEAVED(vsub, SUB_SF, sf, bf); |
| 131 | DEF_TEST_OP_2_INTERLEAVED(vmpy, MULT_SF, sf, bf); |
| 132 | |
| 133 | /****************************************************************************** |
| 134 | * Other tests |
| 135 | *****************************************************************************/ |
| 136 | |
| 137 | static void test_vdmpy_sf_hf(bool acc) |
| 138 | { |
| 139 | memset(expect, 0xff, sizeof(expect)); |
| 140 | |
| 141 | for (int i = 0; i < BUFSIZE; i++) { |
| 142 | HVX_Vector hvx_buffer0 = *(HVX_Vector *)&buffer0[i]; |
| 143 | HVX_Vector hvx_buffer1 = *(HVX_Vector *)&buffer1[i]; |
| 144 | HVX_Vector *hvx_output = (HVX_Vector *)&output[i]; |
| 145 | |
| 146 | uint32_t PREFIL_VAL = 0x111222; |
| 147 | *hvx_output = Q6_V_vsplat_R(PREFIL_VAL); |
| 148 | |
| 149 | if (!acc) { |
| 150 | *hvx_output = Q6_Vsf_vdmpy_VhfVhf(hvx_buffer0, hvx_buffer1); |
| 151 | } else { |
| 152 | *hvx_output = Q6_Vsf_vdmpyacc_VsfVhfVhf(*hvx_output, hvx_buffer0, |
| 153 | hvx_buffer1); |
| 154 | } |
| 155 | |
| 156 | for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { |
| 157 | float a1 = float_hf_to_sf(float_hf(buffer0[i].hf[2 * j + 1])); |
| 158 | float a2 = float_hf_to_sf(float_hf(buffer0[i].hf[2 * j])); |
| 159 | float a3 = float_hf_to_sf(float_hf(buffer1[i].hf[2 * j + 1])); |
| 160 | float a4 = float_hf_to_sf(float_hf(buffer1[i].hf[2 * j])); |
| 161 | /* |
| 162 | * Note, IEEE FP specifies +0.0 + -0.0 == +0.0. So we use -0.0 in |
| 163 | * the default case to preserve the zero sign. |
| 164 | */ |
| 165 | float prev = acc ? float_sf(PREFIL_VAL) : -0.0; |
| 166 | expect[i].sf[j] = raw_sf(CHECK_NAN((a1 * a3) + (a2 * a4) + prev, NAN_SF)); |
| 167 | } |
| 168 | } |
| 169 | check_output_sf(__LINE__, BUFSIZE); |
| 170 | } |
| 171 | |
| 172 | static void test_new(void) |
| 173 | { |
| 174 | asm volatile("r0 = #%2\n" |
| 175 | "v0 = vsplat(r0)\n" |
| 176 | "vmem(%1 + #0) = v0\n" |
| 177 | "r1 = #%3\n" |
| 178 | "v1 = vsplat(r1)\n" |
| 179 | "v2 = vsplat(r1)\n" |
| 180 | "{\n" |
| 181 | " v0.sf = vadd(v1.sf, v2.sf)\n" |
| 182 | " vmem(%0 + #0) = v0.new\n" |
| 183 | "}\n" |
| 184 | : |
| 185 | : "r"(output), "r"(expect), "i"(SF_two), "i"(SF_one) |
| 186 | : "r0", "r1", "v0", "v1", "v2", "memory"); |
| 187 | check_output_w(__LINE__, 1); |
| 188 | } |
| 189 | |
| 190 | int main(void) |
| 191 | { |
| 192 | init_buffers_fp(); |
| 193 | |
| 194 | /* add/sub */ |
| 195 | test_vadd_sf_sf(); |
| 196 | test_vadd_hf_hf(); |
| 197 | test_vsub_sf_sf(); |
| 198 | test_vsub_hf_hf(); |
| 199 | |
| 200 | /* multiply */ |
| 201 | test_vmpy_sf_sf(); |
| 202 | test_vmpy_hf_hf(); |
| 203 | |
| 204 | /* dot product */ |
| 205 | test_vdmpy_sf_hf(false); |
| 206 | test_vdmpy_sf_hf(true); |
| 207 | |
| 208 | test_new(); |
| 209 | |
| 210 | /* min/max */ |
| 211 | test_vfmin_sf_sf(); |
| 212 | test_vfmin_hf_hf(); |
| 213 | test_vfmax_sf_sf(); |
| 214 | test_vfmax_hf_hf(); |
| 215 | |
| 216 | /* bfloat */ |
| 217 | init_buffers_bf(); |
| 218 | test_vmin_bf_bf(); |
| 219 | test_vmax_bf_bf(); |
| 220 | test_vadd_sf_bf(); |
| 221 | test_vsub_sf_bf(); |
| 222 | test_vmpy_sf_bf(); |
| 223 | |
| 224 | puts(err ? "FAIL" : "PASS"); |
| 225 | return err ? 1 : 0; |
| 226 | } |