Skip to content

Commit d8d37f3

Browse files
committed
test: ztest: Port exponential math tests from CMock
Port exponential function tests from CMock/CMocka to Zephyr Ztest framework. Migrates tests from test/cmocka/src/math/arithmetic/exponential.c to test/ztest/unit/math/advanced/functions/test_exponential_ztest.c. Test coverage includes: - sofm_exp_approx(): ULP-based validation of Q28 -> Q19 approximation (256 samples, range -8 to 8) - sofm_exp_fixed(): Fixed-point exponential Q27 -> Q20 - Full range: -16 to 16 (100 samples, coarse grid) - Middle range: -11.5 to 7.6245 (100 samples, fine grid) - sofm_db2lin_fixed(): dB to linear conversion Q24 -> Q20 (100 samples, range -128 to 128 dB) Changes: - Add test_exponential_ztest.c with 3 test functions (291 lines) - Update CMakeLists.txt to build exp_fcn.c and exp_fcn_hifi.c - Add comment noting exp_fcn_hifi.c is Xtensa HiFi-specific - Update testcase.yaml tags: exponential, exp, db2lin - Use Zephyr LOG_INF for diagnostic output instead of printf All tests validate against reference implementations using absolute and relative error tolerances appropriate for fixed-point arithmetic. Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
1 parent b52f0f9 commit d8d37f3

File tree

3 files changed

+300
-1
lines changed

3 files changed

+300
-1
lines changed

test/ztest/unit/math/advanced/functions/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,13 @@ target_compile_definitions(app PRIVATE
3030
target_sources(app PRIVATE
3131
test_scalar_power_ztest.c
3232
test_base2_logarithm_ztest.c
33+
test_exponential_ztest.c
3334
${SOF_ROOT}/src/math/power.c
3435
${SOF_ROOT}/src/math/base2log.c
36+
${SOF_ROOT}/src/math/exp_fcn.c
37+
# Note: exp_fcn_hifi.c is conditionally compiled only for Xtensa HiFi platforms.
38+
# TODO: Enable these tests on Xtensa platforms to also test HiFi-optimized code paths.
39+
${SOF_ROOT}/src/math/exp_fcn_hifi.c
3540
)
3641

3742
# Apply SOF relative path definitions for proper compilation
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
/*
3+
* Copyright(c) 2022-2026 Intel Corporation.
4+
*
5+
* These contents may have been developed with support from one or more Intel-operated
6+
* generative artificial intelligence solutions.
7+
*
8+
* Converted from CMock to Ztest
9+
*
10+
* Original test from sof/test/cmocka/src/math/arithmetic/exponential.c
11+
*
12+
* Author: Shriram Shastry <malladi.sastry@linux.intel.com>
13+
* Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
14+
*/
15+
16+
#include <zephyr/ztest.h>
17+
#include <zephyr/logging/log.h>
18+
#include <sof/math/exp_fcn.h>
19+
#include <sof/audio/format.h>
20+
#include <sof/common.h>
21+
#include <math.h>
22+
#include <rtos/string.h>
23+
24+
LOG_MODULE_REGISTER(test_exponential, LOG_LEVEL_INF);
25+
26+
#define ULP_TOLERANCE 1.0
27+
#define ULP_SCALE 1.9073e-06 /* For exp() output Q13.19, 1 / 2^19 */
28+
#define NUMTESTSAMPLES 256
29+
30+
#define NUMTESTSAMPLES_TEST2 100
31+
#define ABS_DELTA_TOLERANCE_TEST2 2.0e-6
32+
#define REL_DELTA_TOLERANCE_TEST2 1000.0 /* rel. error is large with values near zero */
33+
#define NUMTESTSAMPLES_TEST3 100
34+
#define ABS_DELTA_TOLERANCE_TEST3 2.0e-6
35+
#define REL_DELTA_TOLERANCE_TEST3 10.0e-2
36+
#define SOFM_EXP_FIXED_ARG_MIN -11.5
37+
#define SOFM_EXP_FIXED_ARG_MAX 7.6245
38+
39+
#define NUMTESTSAMPLES_TEST4 100
40+
#define ABS_DELTA_TOLERANCE_TEST4 2.5e-5
41+
#define REL_DELTA_TOLERANCE_TEST4 1000.0 /* rel. error is large with values near zero */
42+
43+
/**
44+
* Saturates input to 32 bits
45+
* @param x Input value
46+
* @return Saturated output value
47+
*/
48+
static int32_t saturate32(int64_t x)
49+
{
50+
if (x < INT32_MIN)
51+
return INT32_MIN;
52+
else if (x > INT32_MAX)
53+
return INT32_MAX;
54+
55+
return x;
56+
}
57+
58+
/**
59+
* Generates linearly spaced values for a vector with end points and number points in
60+
* desired fractional Q-format for 32 bit integer. If the test values exceed int32_t
61+
* range, the values are saturated to INT32_MIN to INT32_MAX range.
62+
*
63+
* @param a First value of test vector
64+
* @param b Last value of test vector
65+
* @param step_count Number of values in vector
66+
* @param point Calculate n-th point of vector 0 .. step_count - 1
67+
* @param qformat Number of fractional bits y in Qx.y format
68+
* @param fout Pointer to calculated test vector value, double
69+
* @param iout Pointer to calculated test vector value, int32_t
70+
*/
71+
static void gen_testvector_linspace_int32(double a, double b, int step_count, int point,
72+
int qformat, double *fout, int32_t *iout)
73+
{
74+
double fstep = (b - a) / (step_count - 1);
75+
double fvalue = a + fstep * point;
76+
int64_t itmp;
77+
78+
itmp = (int64_t)round(fvalue * (double)(1 << qformat));
79+
*iout = saturate32(itmp);
80+
*fout = (double)*iout / (1 << qformat);
81+
}
82+
83+
/**
84+
* Calculate reference exponent value
85+
* @param x Input value
86+
* @param qformat Fractional bits y in Qx.y format
87+
* @return Saturated exponent value to match fractional format
88+
*/
89+
static double ref_exp(double x, int qformat)
90+
{
91+
double yf;
92+
int64_t yi;
93+
94+
yf = exp(x);
95+
yi = yf * (1 << qformat);
96+
97+
if (yi > INT32_MAX)
98+
yi = INT32_MAX;
99+
else if (yi < INT32_MIN)
100+
yi = INT32_MIN;
101+
102+
yf = (double)yi / (1 << qformat);
103+
return yf;
104+
}
105+
106+
/**
107+
* Calculates test exponent function and compares result to reference exponent.
108+
* @param ivalue Fractional format input value Q5.27
109+
* @param iexp_value Fractional format output value Q12.20
110+
* @param abs_delta_max Calculated absolute error
111+
* @param rel_delta_max Calculated relative error
112+
* @param abs_delta_tolerance Tolerance for absolute error
113+
* @param rel_delta_tolerance Tolerance for relative error
114+
*/
115+
static void test_exp_with_input_value(int32_t ivalue, int32_t *iexp_value,
116+
double *abs_delta_max, double *rel_delta_max,
117+
double abs_delta_tolerance, double rel_delta_tolerance)
118+
{
119+
double fvalue, fexp_value, ref_exp_value;
120+
double rel_delta, abs_delta;
121+
double eps = 1e-9;
122+
123+
*iexp_value = sofm_exp_fixed(ivalue);
124+
fvalue = (double)ivalue / (1 << 27); /* Q5.27 */
125+
fexp_value = (double)*iexp_value / (1 << 20); /* Q12.20 */
126+
ref_exp_value = ref_exp(fvalue, 20);
127+
abs_delta = fabs(ref_exp_value - fexp_value);
128+
rel_delta = abs_delta / (ref_exp_value + eps);
129+
130+
if (abs_delta > *abs_delta_max)
131+
*abs_delta_max = abs_delta;
132+
133+
if (rel_delta > *rel_delta_max)
134+
*rel_delta_max = rel_delta;
135+
136+
zassert_true(abs_delta <= abs_delta_tolerance,
137+
"sofm_exp_fixed: Absolute error %g exceeds limit %g, input %g output %g",
138+
abs_delta, abs_delta_tolerance, fvalue, fexp_value);
139+
140+
zassert_true(rel_delta <= rel_delta_tolerance,
141+
"sofm_exp_fixed: Relative error %g exceeds limit %g, input %g output %g",
142+
rel_delta, rel_delta_tolerance, fvalue, fexp_value);
143+
}
144+
145+
/**
146+
* Reference function for dB to linear conversion
147+
* @param x Input value
148+
* @param qformat Fractional bits y in Qx.y format for saturation
149+
* @return Saturated linear value
150+
*/
151+
static double ref_db2lin(double x, int qformat)
152+
{
153+
double fref;
154+
int64_t iref;
155+
156+
fref = pow(10, x / 20);
157+
iref = fref * (1 << qformat);
158+
return (double)saturate32(iref) / (1 << qformat);
159+
}
160+
161+
/**
162+
* @brief Test sofm_exp_approx() function with ULP error validation
163+
*
164+
* This test validates the sofm_exp_approx() exponential approximation function
165+
* against the C standard library exp() function. It tests 256 linearly spaced
166+
* input values and checks that the ULP (Unit in the Last Place) error stays
167+
* within acceptable tolerance.
168+
*
169+
* Input values: Q28 format, range -8 to 8
170+
* Result: Q19 format
171+
* Validation: ULP error < 1.0 ULP
172+
*/
173+
ZTEST(math_advanced_functions_suite, test_function_sofm_exp_approx)
174+
{
175+
int32_t accum;
176+
int i;
177+
double a_i;
178+
double max_ulp = 0;
179+
double ulp;
180+
double a_tmp = -8;
181+
double b_tmp = 8;
182+
int32_t b_i;
183+
184+
for (i = 0; i < NUMTESTSAMPLES; i++) {
185+
gen_testvector_linspace_int32(a_tmp, b_tmp, NUMTESTSAMPLES, i, 28, &a_i, &b_i);
186+
accum = sofm_exp_approx(b_i);
187+
ulp = fabs(exp(a_i) - (double)accum / (1 << 19)) / ULP_SCALE;
188+
if (ulp > max_ulp)
189+
max_ulp = ulp;
190+
191+
zassert_true(ulp <= ULP_TOLERANCE,
192+
"sofm_exp_approx: ULP %.16f exceeds tolerance, value=%.16f, exp=%.16f",
193+
ulp, (double)b_i / (1 << 28), (double)accum / (1 << 19));
194+
}
195+
196+
LOG_INF("Worst-case ULP: %g ULP_SCALE %g", max_ulp, ULP_SCALE);
197+
}
198+
199+
/**
200+
* @brief Test sofm_exp_fixed() function with absolute and relative error validation
201+
*
202+
* This test validates the sofm_exp_fixed() fixed-point exponential function
203+
* against a reference implementation. It performs two sub-tests with different
204+
* input ranges and tolerance requirements.
205+
*
206+
* Sub-test 1: Coarse grid across max range
207+
* - Input values: Q27 format, range -16 to 16
208+
* - Result: Q20 format
209+
* - Tolerances: abs 2.0e-6, rel 1000.0
210+
*
211+
* Sub-test 2: Fine grid across typical range
212+
* - Input values: Q27 format, range -11.5 to 7.6245
213+
* - Result: Q20 format
214+
* - Tolerances: abs 2.0e-6, rel 10.0e-2
215+
*/
216+
ZTEST(math_advanced_functions_suite, test_function_sofm_exp_fixed)
217+
{
218+
double rel_delta_max, abs_delta_max;
219+
double tmp;
220+
int32_t ivalue, iexp_value;
221+
int i;
222+
223+
/* Test max int32_t range with coarse grid */
224+
rel_delta_max = 0;
225+
abs_delta_max = 0;
226+
for (i = 0; i < NUMTESTSAMPLES_TEST2; i++) {
227+
gen_testvector_linspace_int32(-16, 16, NUMTESTSAMPLES_TEST2, i, 27, &tmp, &ivalue);
228+
test_exp_with_input_value(ivalue, &iexp_value, &abs_delta_max, &rel_delta_max,
229+
ABS_DELTA_TOLERANCE_TEST2, REL_DELTA_TOLERANCE_TEST2);
230+
}
231+
232+
LOG_INF("Absolute max error was %.6e (max range)", abs_delta_max);
233+
LOG_INF("Relative max error was %.6e (max range)", rel_delta_max);
234+
235+
/* Test max int32_t middle range with fine grid */
236+
rel_delta_max = 0;
237+
abs_delta_max = 0;
238+
for (i = 0; i < NUMTESTSAMPLES_TEST3; i++) {
239+
gen_testvector_linspace_int32(SOFM_EXP_FIXED_ARG_MIN, SOFM_EXP_FIXED_ARG_MAX,
240+
NUMTESTSAMPLES_TEST3, i, 27, &tmp, &ivalue);
241+
test_exp_with_input_value(ivalue, &iexp_value, &abs_delta_max, &rel_delta_max,
242+
ABS_DELTA_TOLERANCE_TEST3, REL_DELTA_TOLERANCE_TEST3);
243+
}
244+
245+
LOG_INF("Absolute max error was %.6e (middle)", abs_delta_max);
246+
LOG_INF("Relative max error was %.6e (middle)", rel_delta_max);
247+
}
248+
249+
/**
250+
* @brief Test sofm_db2lin_fixed() function for dB to linear conversion
251+
*
252+
* This test validates the sofm_db2lin_fixed() function that converts decibel
253+
* values to linear scale using fixed-point arithmetic. It compares against
254+
* a reference implementation using floating-point pow(10, x/20).
255+
*
256+
* Input values: Q24 format, range -128 to 128 dB
257+
* Result: Q20 format
258+
* Tolerances: abs 2.5e-5, rel 1000.0
259+
*/
260+
ZTEST(math_advanced_functions_suite, test_function_sofm_db2lin_fixed)
261+
{
262+
double abs_delta, rel_delta, abs_delta_max, rel_delta_max;
263+
double fin, fout, fref;
264+
double eps = 1e-9;
265+
int32_t iin, iout;
266+
int i;
267+
268+
rel_delta_max = 0;
269+
abs_delta_max = 0;
270+
for (i = 0; i < NUMTESTSAMPLES_TEST4; i++) {
271+
gen_testvector_linspace_int32(-128, 128, NUMTESTSAMPLES_TEST4, i, 24, &fin, &iin);
272+
iout = sofm_db2lin_fixed(iin);
273+
fout = (double)iout / (1 << 20);
274+
fref = ref_db2lin(fin, 20);
275+
abs_delta = fabs(fref - fout);
276+
rel_delta = abs_delta / (fref + eps);
277+
if (abs_delta > abs_delta_max)
278+
abs_delta_max = abs_delta;
279+
280+
if (rel_delta > rel_delta_max)
281+
rel_delta_max = rel_delta;
282+
283+
zassert_true(abs_delta <= ABS_DELTA_TOLERANCE_TEST4,
284+
"sofm_db2lin_fixed: Absolute error %g exceeds limit %g, input %g output %g",
285+
abs_delta, ABS_DELTA_TOLERANCE_TEST4, fin, fout);
286+
287+
zassert_true(rel_delta <= REL_DELTA_TOLERANCE_TEST4,
288+
"sofm_db2lin_fixed: Relative error %g exceeds limit %g, input %g output %g",
289+
rel_delta, REL_DELTA_TOLERANCE_TEST4, fin, fout);
290+
}
291+
292+
LOG_INF("Absolute max error was %.6e", abs_delta_max);
293+
LOG_INF("Relative max error was %.6e", rel_delta_max);
294+
}

test/ztest/unit/math/advanced/functions/testcase.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
tests:
1212
sof.unit.math.advanced.functions:
13-
tags: math advanced functions power logarithm base2
13+
tags: math advanced functions power logarithm base2 exponential exp db2lin
1414
platform_allow: native_sim
1515
integration_platforms:
1616
- native_sim

0 commit comments

Comments
 (0)