Skip to content

Commit 408884d

Browse files
committed
zdsp: add a file with macro implementation of fixed point functions
Saturation logic is supported, but not rounding. This helps as a support for inputting literal values: q15_t first = Q15f(0.23); This permits to store Q values in consts expressions: #define VAL(x) SUBq7(Q7f(1.0), Q7f(x / M_PI)) const q7_t table[] = { VAL(1.32), VAL(1.42), VAL(0.8) }; This permits to use fixed point arithmetics when a zdsp back-end is not available, such as light computation in drivers subitted upstream. Signed-off-by: Josuah Demangeon <me@josuah.net>
1 parent 87b82f5 commit 408884d

File tree

8 files changed

+613
-3
lines changed

8 files changed

+613
-3
lines changed

include/zephyr/dsp/macros.h

Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
/* Copyright (c) 2024 tinyVision.ai Inc.
2+
* SPDX-License-Identifier: Apache-2.0
3+
*/
4+
5+
#ifndef INCLUDE_ZEPHYR_DSP_MACROS_H_
6+
#define INCLUDE_ZEPHYR_DSP_MACROS_H_
7+
8+
#include <stdint.h>
9+
10+
#include <zephyr/dsp/types.h>
11+
#include <zephyr/sys/util.h>
12+
13+
/**
14+
* @brief Definition of the minimum and maximum values of the internal representation
15+
*
16+
* @note The value represented is always between -1.0 and 1.0 for any storage size.
17+
* Scaling variables imported before using them in fixed-points is required.
18+
*
19+
* @{
20+
*/
21+
22+
/** Minimum internal value for Q.7. */
23+
#define MINq7 INT8_MIN
24+
25+
/** Minimum internal value for Q.15. */
26+
#define MINq15 INT16_MIN
27+
28+
/** Minimum internal value for Q.31. */
29+
#define MINq31 INT32_MIN
30+
31+
/** Maximum internal value for Q.7. */
32+
#define MAXq7 INT8_MAX
33+
34+
/** Maximum internal value for Q.15. */
35+
#define MAXq15 INT16_MAX
36+
37+
/** Maximum internal value for Q.31. */
38+
#define MAXq31 INT32_MAX
39+
40+
/* @}
41+
*/
42+
43+
/**
44+
* @brief Clamp a fixed-point value to -1.0 to 1.0.
45+
*
46+
* @note Useful for internal use, or when breaking the abstraction barrier.
47+
*
48+
* @{
49+
*/
50+
51+
/** Enforce the a Q.7 fixed-point to be between -1.0 and 1.0 */
52+
#define CLAMPq7(q7) ((q7_t)CLAMP((q7), MINq7, MAXq7))
53+
54+
/** Enforce the a Q.15 fixed-point to be between -1.0 and 1.0 */
55+
#define CLAMPq15(q15) ((q15_t)CLAMP((q15), MINq15, MAXq15))
56+
57+
/** Enforce the a Q.31 fixed-point to be between -1.0 and 1.0 */
58+
#define CLAMPq31(q31) ((q31_t)CLAMP((q31), MINq31, MAXq31))
59+
60+
/* @}
61+
*/
62+
63+
/**
64+
* @brief Construct a fixed-point number out of a floating point number.
65+
*
66+
* The input float can be an expression or a single number.
67+
* If it is below -1.0 or above 1.0, it is adjusted to fit the -1.0 to 1.0 range.
68+
*
69+
* @example "Q15(-1.0) // Minimum value"
70+
* @example "Q31(1.0) // Maximum value"
71+
* @example "Q15(1.3) // Will become 1.0"
72+
* @example "Q7(f - 1.0)"
73+
* @example "Q15(2.0 / 3.1415)"
74+
*
75+
* @param f Floating-point number to convert into a fixed-point number.
76+
*
77+
* @{
78+
*/
79+
80+
/** Construct a Q.7 fixed-point: 1 bit for the sign, 7 bits of fractional part */
81+
#define Q7f(f) CLAMPq7((f) * (1ll << 7))
82+
83+
/** Construct a Q.15 fixed-point: 1 bit for the sign, 15 bits of fractional part */
84+
#define Q15f(f) CLAMPq15((f) * (1ll << 15))
85+
86+
/** Construct a Q.31 fixed-point: 1 bit for the sign, 31 bits of fractional part */
87+
#define Q31f(f) CLAMPq31((f) * (1ll << 31))
88+
89+
/* @}
90+
*/
91+
92+
/**
93+
* @brief Construct a fixed-point out of an integer and a scale.
94+
*
95+
* This permits to work with numbers above the -1.0 to 1.0 range:
96+
* The minimum and maximum possible value of the input number are specified.
97+
* The input number is then scaled so that the minimum value becomes -1.0 and
98+
* maximum value becomes 1.0.
99+
* Once the computation is done in fixed piotn, it is popssible to get an
100+
* unscaled value with @ref INTq7, @ref INtq15 and @rev INTq31.
101+
*
102+
* @example "Q15i(temperature, -20, 200)"
103+
* @example "Q15i(angle, 0, 360)"
104+
* @example "Q15i(voltage, V_MIN, V_MAX)"
105+
*
106+
* @param i Input number to convert to a fixed-point representation
107+
* @param min Minimum value that @p i can take.
108+
* The same minimum value is used to convert the numbers back.
109+
* @param max Maximum value that @p i can take.
110+
* The same maximum value is used to convert the numbers back.
111+
*
112+
* @{
113+
*/
114+
115+
/** Build a Q.7 number by scaling @p i range from [min, max] (at most [ , ]) to [-1.0, 1.0] */
116+
#define Q7i(i, min, max) CLAMPq7(SCALE((i), (min), (max), MINq7, MAXq7))
117+
118+
/** Build a Q.15 number by scaling @p i range from [min, max] to [-1.0, 1.0] */
119+
#define Q15i(i, min, max) CLAMPq15(SCALE((i), (min), (max), MINq15, MAXq15))
120+
121+
/** Build a Q.31 number by scaling @p i range from [min, max] to [-1.0, 1.0] */
122+
#define Q31i(i, min, max) CLAMPq31(SCALE((i), (min), (max), MINq31, MAXq31))
123+
124+
/* @}
125+
*/
126+
127+
/**
128+
* @brief Convert fixed-points from one size to another
129+
*
130+
* This permits to increase or decrease the precision by switching between
131+
* smaller and larger representation.
132+
* The values represented does not change except due to loss of precision.
133+
* The minimum value remains -1.0 and maximum value remains 1.0.
134+
* Only the internal representation is scaled.
135+
*
136+
* @{
137+
*/
138+
139+
/** Build a Q.7 number out of a Q.15 number, losing 8 bits of precision */
140+
#define Q7q15(q15) (q7_t)((q15) / (1 << 8))
141+
142+
/** Build a Q.7 number out of a Q.31 number, losing 24 bits of precision */
143+
#define Q7q31(q31) (q7_t)((q31) / (1 << 24))
144+
145+
/** Build a Q.15 number out of a Q.7 number, gaining 8 bits of precision */
146+
#define Q15q7(q7) CLAMPq15((q15_t)(q7) * (1 << 8))
147+
148+
/** Build a Q.15 number out of a Q.31 number, losing 16 bits of precision */
149+
#define Q15q31(q31) (q15_t)((q31) / (1 << 16))
150+
151+
/** Build a Q.31 number out of a Q.7 number, gaining 24 bits of precision */
152+
#define Q31q7(q7) CLAMPq31((q31_t)(q7) * (1 << 24))
153+
154+
/** Build a Q.31 number out of a Q.15 number, gaining 16 bits of precision */
155+
#define Q31q15(q15) CLAMPq31((q31_t)(q15) * (1 << 16))
156+
157+
/* @}
158+
*/
159+
160+
/**
161+
* @brief Convert a fixed-point number back to an natural representation.
162+
*
163+
* This permits to extract a result value out of a fixed-point number,
164+
* to reverse the effect of @ref Q7i, @ref Q15i and @ref Q31i.
165+
*
166+
* @param i The fixed-point number to convert to a natural number.
167+
* @param min The minimum value specified to create the fixed-point.
168+
* @param max The maximum value specified to create the fixed-point.
169+
*
170+
* @{
171+
*/
172+
173+
/** Convert a Q.7 fixed-point number to a natural integer */
174+
#define INTq7(i, min, max) SCALE((i), MINq7, MAXq7, (min), (max))
175+
176+
/** Convert a Q.15 fixed-point number to a natural integer */
177+
#define INTq15(i, min, max) SCALE((i), MINq15, MAXq15, (min), (max))
178+
179+
/** Convert a Q.31 fixed-point number to a natural integer */
180+
#define INTq31(i, min, max) SCALE((i), MINq31, MAXq31, (min), (max))
181+
182+
/* @}
183+
*/
184+
185+
/**
186+
* @brief Add two fixed-point numbers together.
187+
*
188+
* Saturation logic is applied, so number out of ranges will be converted
189+
* to the minimum -1.0 or maximum 1.0 value instead of overflowing.
190+
*
191+
* @note If two fixed-point numbers are to be subtracted into a larger type
192+
* such as Q.7 + Q.7 = Q.15, the C operator @c + can be used instead.
193+
*
194+
* @param a First number to add.
195+
* @param b Second number to add.
196+
*
197+
* @{
198+
*/
199+
200+
/** Sum two Q.7 numbers and produce a Q.7 result */
201+
#define ADDq7(a, b) CLAMPq7((int16_t)(a) + (int16_t)(b))
202+
203+
/** Sum two Q.15 numbers and produce a Q.15 result */
204+
#define ADDq15(a, b) CLAMPq15((int32_t)(a) + (int32_t)(b))
205+
206+
/** Sum two Q.31 numbers and produce a Q.31 result */
207+
#define ADDq31(a, b) CLAMPq31((int64_t)(a) + (int64_t)(b))
208+
209+
/* @}
210+
*/
211+
212+
/**
213+
* @brief Subtract a fixed-point number to another.
214+
*
215+
* Saturation logic is applied, so number out of ranges will be converted
216+
* to the minimum 1.0 or maximum -1.0 value instead of overflowing.
217+
*
218+
* @note If two fixed-point numbers are to be subtracted into a larger type
219+
* such as Q.7 - Q.7 = Q.15, the C operator @c - can be used instead.
220+
*
221+
* @param a First number to add.
222+
* @param a Second number to add.
223+
*
224+
* @{
225+
*/
226+
227+
/** Subtract two Q.7 numbers and produce a Q.7 result */
228+
#define SUBq7(a, b) CLAMPq7((int16_t)(a) - (int16_t)(b))
229+
230+
/** Subtract two Q.15 numbers and produce a Q.15 result */
231+
#define SUBq15(a, b) CLAMPq15((int32_t)(a) - (int32_t)(b))
232+
233+
/** Subtract two Q.31 numbers and produce a Q.31 result */
234+
#define SUBq31(a, b) CLAMPq31((int64_t)(a) - (int64_t)(b))
235+
236+
/* @}
237+
*/
238+
239+
/**
240+
* @brief Multiply two fixed-point numbers together.
241+
*
242+
* Saturation logic is applied, so number out of range will be converted
243+
* to handle the edge case Q#f(-1.0) * Q#f(-1.0) = Q#f(1.0)
244+
*
245+
* @note This implementation does not perform rounding.
246+
*
247+
* @param a First number to add.
248+
* @param b Second number to add.
249+
*
250+
* @{
251+
*/
252+
253+
/** Multiply two Q.7 numbers and produce a Q.7 result */
254+
#define MULq7(a, b) CLAMPq7(((int16_t)(a) * (int16_t)(b)) / (1 << 7))
255+
256+
/** Multiply two Q.15 numbers and produce a Q.15 result */
257+
#define MULq15(a, b) CLAMPq15(((int32_t)(a) * (int32_t)(b)) / (1 << 15))
258+
259+
/** Multiply two Q.31 numbers and produce a Q.31 result */
260+
#define MULq31(a, b) CLAMPq31(((int64_t)(a) * (int64_t)(b)) / (1 << 31))
261+
262+
/* @}
263+
*/
264+
265+
/**
266+
* @brief Divide two fixed-point numbers together.
267+
*
268+
* Saturation logic is applied, so number out of ranges will be converted
269+
* to the minimum -1.0 or maximum 1.0 value instead of overflowing.
270+
*
271+
* @note This implementation does not perform rounding.
272+
*
273+
* @param a Numerator of the division.
274+
* @param b Denominator of the division.
275+
*
276+
* @{
277+
*/
278+
279+
/** Divide a Q.7 number and produce a Q.7 result */
280+
#define DIVq7(a, b) CLAMPq7((a) * (1 << 7) / (b))
281+
282+
/** Divide a Q.15 number and produce a Q.15 result */
283+
#define DIVq15(a, b) CLAMPq15((a) * (1 << 15) / (b))
284+
285+
/** Divide a Q.31 number and produce a Q.31 result */
286+
#define DIVq31(a, b) CLAMPq31((a) * (1 << 31) / (b))
287+
288+
/* @}
289+
*/
290+
291+
/**
292+
* @brief Apply the opposite value of the fixed-point number.
293+
*
294+
* Saturation logic is applied, as the most negative number could
295+
* overflow internally if converted to positive.
296+
*
297+
* @param a Number to get the opposite value for
298+
*
299+
* @{
300+
*/
301+
302+
/** Get the negation of a Q.7 number and produce a Q.7 result */
303+
#define NEGq7(a) (q7_t)((a) == MINq7 ? MAXq7 : -(a))
304+
305+
/** Get the negation of a Q.15 number and produce a Q.15 result */
306+
#define NEGq15(a) (q15_t)((a) == MINq15 ? MAXq15 : -(a))
307+
308+
/** Get the negation of a Q.15 number and produce a Q.15 result */
309+
#define NEGq31(a) (q31_t)((a) == MINq31 ? MAXq31 : -(a))
310+
311+
/* @}
312+
*/
313+
314+
/**
315+
* @brief Get the absolute value of a fixed-point number.
316+
*
317+
* Saturation logic is applied, as the most negative number overflows if
318+
* converted to positive.
319+
*
320+
* @param a Number to get the absolute value for.
321+
*
322+
* @{
323+
*/
324+
325+
/** Get the absolute value of a Q.7 number and produce a Q.7 result */
326+
#define ABSq7(a) (q7_t)((a) < 0 ? NEGq7(a) : (a))
327+
328+
/** Get the absolute value of a Q.15 number and produce a Q.15 result */
329+
#define ABSq15(a) (q15_t)((a) < 0 ? NEGq15(a) : (a))
330+
331+
/** Get the absolute value of a Q.31 number and produce a Q.31 result */
332+
#define ABSq31(a) (q31_t)((a) < 0 ? NEGq31(a) : (a))
333+
334+
/* @}
335+
*/
336+
337+
#endif /* INCLUDE_ZEPHYR_DSP_MACROS_H_ */

tests/lib/sys_util/src/main.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ ZTEST(sys_util, test_SCALE)
2727
for (int i = 1; i < 61; i++) {
2828
int o = 61 - i;
2929
int64_t imin = -(1ll << i);
30-
int64_t imax = +(1ll << i);
30+
int64_t imax = (1ll << i);
3131
int64_t omin = -(1ll << o);
32-
int64_t omax = +(1ll << o);
32+
int64_t omax = (1ll << o);
3333

3434
/* Special case: the output range can be [0, 0] */
3535

@@ -43,7 +43,7 @@ ZTEST(sys_util, test_SCALE)
4343
zassert_equal(SCALE(imin, imin, 0, 0, 0), 0);
4444
zassert_equal(SCALE(0, imin, 0, 0, 0), 0);
4545

46-
/* Test the extreme of all known cases */
46+
/* Test the extreme cases */
4747

4848
zassert_equal(SCALE(imin, imin, imax, omin, omax), omin);
4949
zassert_equal(SCALE(0, imin, imax, omin, omax), 0);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
5+
project(cmsis_dsp_macros)
6+
7+
target_sources(app PRIVATE
8+
src/q7.c
9+
)

tests/subsys/dsp/macros/prj.conf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
CONFIG_ZTEST=y
2+
CONFIG_DSP=y

tests/subsys/dsp/macros/src/q15.c

Whitespace-only changes.

tests/subsys/dsp/macros/src/q31.c

Whitespace-only changes.

0 commit comments

Comments
 (0)