Sound Open Firmware
Loading...
Searching...
No Matches
arm_simd.h
Go to the documentation of this file.
1/* SPDX-License-Identifier: BSD-3-Clause
2 *
3 * Copyright(c) 2026 Sound Open Firmware. All rights reserved.
4 *
5 * Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
6 */
7
8#ifndef __SOF_AUDIO_ARM_SIMD_H__
9#define __SOF_AUDIO_ARM_SIMD_H__
10
11#include <stdint.h>
12#include <stdbool.h>
13
14#if (defined(__arm__) || defined(__thumb__) || defined(CONFIG_ARM)) && \
15 defined(__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)
16
17#if defined(__ZEPHYR__)
18#include <cmsis_core.h>
19#endif
20
21/*
22 * Branchless Saturation Primitives using ARM SSAT instruction
23 */
24static inline int16_t arm_sat_s16(int32_t val)
25{
26#if defined(__GNUC__)
27 int32_t res;
28 __asm__("ssat %0, #16, %1" : "=r"(res) : "r"(val));
29 return (int16_t)res;
30#elif defined(__SSAT)
31 return (int16_t)__SSAT(val, 16);
32#else
33 if (val > 32767)
34 return 32767;
35 if (val < -32768)
36 return -32768;
37 return (int16_t)val;
38#endif
39}
40
41static inline int32_t arm_sat_s24(int32_t val)
42{
43#if defined(__GNUC__)
44 int32_t res;
45 __asm__("ssat %0, #24, %1" : "=r"(res) : "r"(val));
46 return res;
47#elif defined(__SSAT)
48 return __SSAT(val, 24);
49#else
50 if (val > 8388607)
51 return 8388607;
52 if (val < -8388608)
53 return -8388608;
54 return val;
55#endif
56}
57
58static inline int32_t arm_sat_q31(int64_t val)
59{
60 if (__builtin_expect(val >= (int64_t)INT32_MIN && val <= (int64_t)INT32_MAX, 1))
61 return (int32_t)val;
62 return (val > INT32_MAX) ? INT32_MAX : INT32_MIN;
63}
64
65/*
66 * Dual 16-bit Arithmetic
67 */
68static inline uint32_t arm_qadd16(uint32_t a, uint32_t b)
69{
70#if defined(__GNUC__)
71 uint32_t res;
72 __asm__("qadd16 %0, %1, %2" : "=r"(res) : "r"(a), "r"(b));
73 return res;
74#elif defined(__QADD16)
75 return __QADD16(a, b);
76#else
77 int16_t a_l = (int16_t)(a & 0xffff);
78 int16_t a_h = (int16_t)(a >> 16);
79 int16_t b_l = (int16_t)(b & 0xffff);
80 int16_t b_h = (int16_t)(b >> 16);
81 int16_t r_l = arm_sat_s16((int32_t)a_l + b_l);
82 int16_t r_h = arm_sat_s16((int32_t)a_h + b_h);
83 return ((uint32_t)(uint16_t)r_h << 16) | (uint16_t)r_l;
84#endif
85}
86
87static inline uint32_t arm_qsub16(uint32_t a, uint32_t b)
88{
89#if defined(__GNUC__)
90 uint32_t res;
91 __asm__("qsub16 %0, %1, %2" : "=r"(res) : "r"(a), "r"(b));
92 return res;
93#elif defined(__QSUB16)
94 return __QSUB16(a, b);
95#else
96 int16_t a_l = (int16_t)(a & 0xffff);
97 int16_t a_h = (int16_t)(a >> 16);
98 int16_t b_l = (int16_t)(b & 0xffff);
99 int16_t b_h = (int16_t)(b >> 16);
100 int16_t r_l = arm_sat_s16((int32_t)a_l - b_l);
101 int16_t r_h = arm_sat_s16((int32_t)a_h - b_h);
102 return ((uint32_t)(uint16_t)r_h << 16) | (uint16_t)r_l;
103#endif
104}
105
106static inline int32_t arm_qadd(int32_t a, int32_t b)
107{
108#if defined(__GNUC__)
109 int32_t res;
110 __asm__("qadd %0, %1, %2" : "=r"(res) : "r"(a), "r"(b));
111 return res;
112#elif defined(__QADD)
113 return __QADD(a, b);
114#else
115 int64_t sum = (int64_t)a + b;
116 return arm_sat_q31(sum);
117#endif
118}
119
120static inline int32_t arm_qsub(int32_t a, int32_t b)
121{
122#if defined(__GNUC__)
123 int32_t res;
124 __asm__("qsub %0, %1, %2" : "=r"(res) : "r"(a), "r"(b));
125 return res;
126#elif defined(__QSUB)
127 return __QSUB(a, b);
128#else
129 int64_t diff = (int64_t)a - b;
130 return arm_sat_q31(diff);
131#endif
132}
133
134/*
135 * Signed 16-bit Multiply
136 */
137static inline int32_t arm_smulbb(int32_t a, int32_t b)
138{
139#if defined(__GNUC__)
140 int32_t res;
141 __asm__("smulbb %0, %1, %2" : "=r"(res) : "r"(a), "r"(b));
142 return res;
143#else
144 return (int32_t)(int16_t)(a & 0xffff) * (int16_t)(b & 0xffff);
145#endif
146}
147
148static inline int32_t arm_smultt(int32_t a, int32_t b)
149{
150#if defined(__GNUC__)
151 int32_t res;
152 __asm__("smultt %0, %1, %2" : "=r"(res) : "r"(a), "r"(b));
153 return res;
154#else
155 return (int32_t)(int16_t)(a >> 16) * (int16_t)(b >> 16);
156#endif
157}
158
159/*
160 * Signed Most Significant Word Multiply with Rounding (SMMULR)
161 * Computes: (a * b + 0x80000000) >> 32 in a single clock cycle.
162 * Ideal for Q1.31 audio scaling.
163 */
164static inline int32_t arm_smmulr(int32_t a, int32_t b)
165{
166#if defined(__GNUC__)
167 int32_t res;
168 __asm__("smmulr %0, %1, %2" : "=r"(res) : "r"(a), "r"(b));
169 return res;
170#else
171 return (int32_t)(((int64_t)a * b + (1LL << 31)) >> 32);
172#endif
173}
174
175/*
176 * 64-bit Multiply-Accumulate (SMLAL)
177 * Computes: acc += (int64_t)a * b in 1 cycle throughput.
178 */
179static inline int64_t arm_smlal(int64_t acc, int32_t a, int32_t b)
180{
181#if defined(__GNUC__)
182 uint32_t lo = (uint32_t)acc;
183 int32_t hi = (int32_t)(acc >> 32);
184 __asm__("smlal %0, %1, %2, %3" : "+r"(lo), "+r"(hi) : "r"(a), "r"(b));
185 return ((int64_t)hi << 32) | lo;
186#else
187 return acc + ((int64_t)a * b);
188#endif
189}
190
191/*
192 * 32x16 Word-Halfword Multiply Accumulate (SMLAWB / SMLAWT)
193 */
194static inline int32_t arm_smlawb(int32_t acc, int32_t a, int32_t b)
195{
196#if defined(__GNUC__)
197 int32_t res;
198 __asm__("smlawb %0, %1, %2, %3" : "=r"(res) : "r"(a), "r"(b), "r"(acc));
199 return res;
200#else
201 return acc + (int32_t)(((int64_t)a * (int16_t)(b & 0xffff)) >> 16);
202#endif
203}
204
205static inline int32_t arm_smlawt(int32_t acc, int32_t a, int32_t b)
206{
207#if defined(__GNUC__)
208 int32_t res;
209 __asm__("smlawt %0, %1, %2, %3" : "=r"(res) : "r"(a), "r"(b), "r"(acc));
210 return res;
211#else
212 return acc + (int32_t)(((int64_t)a * (int16_t)(b >> 16)) >> 16);
213#endif
214}
215
216/*
217 * Pack Halfwords (PKHBT / PKHTB)
218 */
219#if defined(__GNUC__)
220#define arm_pkhbt(a, b, lsl_shift) \
221__extension__ ({ \
222 uint32_t _res; \
223 uint32_t _a = (a); \
224 uint32_t _b = (b); \
225 __asm__("pkhbt %0, %1, %2, lsl %3" : "=r"(_res) : "r"(_a), "r"(_b), "I"(lsl_shift)); \
226 _res; \
227})
228#elif defined(__PKHBT)
229#define arm_pkhbt(a, b, lsl_shift) __PKHBT(a, b, lsl_shift)
230#else
231static inline uint32_t arm_pkhbt(uint32_t a, uint32_t b, uint32_t lsl_shift)
232{
233 return (a & 0xffff) | ((b << lsl_shift) & 0xffff0000);
234}
235#endif
236
237/*
238 * Dual 16-bit Multiply-Accumulate (SMLAD / SMLALD)
239 */
240static inline int32_t arm_smlad(uint32_t a, uint32_t b, int32_t acc)
241{
242#if defined(__GNUC__)
243 int32_t res;
244 __asm__("smlad %0, %1, %2, %3" : "=r"(res) : "r"(a), "r"(b), "r"(acc));
245 return res;
246#elif defined(__SMLAD)
247 return __SMLAD(a, b, acc);
248#else
249 int16_t a0 = (int16_t)(a & 0xffff);
250 int16_t a1 = (int16_t)(a >> 16);
251 int16_t b0 = (int16_t)(b & 0xffff);
252 int16_t b1 = (int16_t)(b >> 16);
253 return acc + (int32_t)a0 * b0 + (int32_t)a1 * b1;
254#endif
255}
256
257static inline int64_t arm_smlald(uint32_t a, uint32_t b, int64_t acc)
258{
259#if defined(__GNUC__)
260 uint32_t lo = (uint32_t)acc;
261 int32_t hi = (int32_t)(acc >> 32);
262 __asm__("smlald %0, %1, %2, %3" : "+r"(lo), "+r"(hi) : "r"(a), "r"(b));
263 return ((int64_t)hi << 32) | lo;
264#elif defined(__SMLALD)
265 return __SMLALD(a, b, acc);
266#else
267 int16_t a0 = (int16_t)(a & 0xffff);
268 int16_t a1 = (int16_t)(a >> 16);
269 int16_t b0 = (int16_t)(b & 0xffff);
270 int16_t b1 = (int16_t)(b >> 16);
271 return acc + ((int64_t)a0 * b0) + ((int64_t)a1 * b1);
272#endif
273}
274
275/*
276 * Sign Extend Halfword (SXTH)
277 */
278static inline int32_t arm_sxth(int32_t val)
279{
280#if defined(__GNUC__)
281 int32_t res;
282 __asm__("sxth %0, %1" : "=r"(res) : "r"(val));
283 return res;
284#elif defined(__SXTH)
285 return __SXTH(val);
286#else
287 return (int32_t)(int16_t)(val & 0xffff);
288#endif
289}
290
291/*
292 * Count Leading Zeros
293 */
294static inline uint32_t arm_clz(uint32_t val)
295{
296 return (uint32_t)__builtin_clz(val);
297}
298
299#else /* Non-ARM fallback */
300
301static inline int16_t arm_sat_s16(int32_t val)
302{
303 if (val > 32767) return 32767;
304 if (val < -32768) return -32768;
305 return (int16_t)val;
306}
307
308static inline int32_t arm_sat_s24(int32_t val)
309{
310 if (val > 8388607) return 8388607;
311 if (val < -8388608) return -8388608;
312 return val;
313}
314
315static inline int32_t arm_sat_q31(int64_t val)
316{
317 if (val > INT32_MAX) return INT32_MAX;
318 if (val < INT32_MIN) return INT32_MIN;
319 return (int32_t)val;
320}
321
322static inline uint32_t arm_qadd16(uint32_t a, uint32_t b)
323{
324 int16_t a_l = (int16_t)(a & 0xffff);
325 int16_t a_h = (int16_t)(a >> 16);
326 int16_t b_l = (int16_t)(b & 0xffff);
327 int16_t b_h = (int16_t)(b >> 16);
328 int16_t r_l = arm_sat_s16((int32_t)a_l + b_l);
329 int16_t r_h = arm_sat_s16((int32_t)a_h + b_h);
330 return ((uint32_t)(uint16_t)r_h << 16) | (uint16_t)r_l;
331}
332
333static inline uint32_t arm_qsub16(uint32_t a, uint32_t b)
334{
335 int16_t a_l = (int16_t)(a & 0xffff);
336 int16_t a_h = (int16_t)(a >> 16);
337 int16_t b_l = (int16_t)(b & 0xffff);
338 int16_t b_h = (int16_t)(b >> 16);
339 int16_t r_l = arm_sat_s16((int32_t)a_l - b_l);
340 int16_t r_h = arm_sat_s16((int32_t)a_h - b_h);
341 return ((uint32_t)(uint16_t)r_h << 16) | (uint16_t)r_l;
342}
343
344static inline int32_t arm_qadd(int32_t a, int32_t b)
345{
346 return arm_sat_q31((int64_t)a + b);
347}
348
349static inline int32_t arm_qsub(int32_t a, int32_t b)
350{
351 return arm_sat_q31((int64_t)a - b);
352}
353
354static inline int32_t arm_smulbb(int32_t a, int32_t b)
355{
356 return (int32_t)(int16_t)(a & 0xffff) * (int16_t)(b & 0xffff);
357}
358
359static inline int32_t arm_smultt(int32_t a, int32_t b)
360{
361 return (int32_t)(int16_t)(a >> 16) * (int16_t)(b >> 16);
362}
363
364static inline int32_t arm_smmulr(int32_t a, int32_t b)
365{
366 return (int32_t)(((int64_t)a * b + (1LL << 31)) >> 32);
367}
368
369static inline int64_t arm_smlal(int64_t acc, int32_t a, int32_t b)
370{
371 return acc + ((int64_t)a * b);
372}
373
374static inline uint32_t arm_pkhbt(uint32_t a, uint32_t b, uint32_t lsl_shift)
375{
376 return (a & 0xffff) | ((b << lsl_shift) & 0xffff0000);
377}
378
379static inline int32_t arm_smlad(uint32_t a, uint32_t b, int32_t acc)
380{
381 int16_t a0 = (int16_t)(a & 0xffff);
382 int16_t a1 = (int16_t)(a >> 16);
383 int16_t b0 = (int16_t)(b & 0xffff);
384 int16_t b1 = (int16_t)(b >> 16);
385 return acc + (int32_t)a0 * b0 + (int32_t)a1 * b1;
386}
387
388static inline int64_t arm_smlald(uint32_t a, uint32_t b, int64_t acc)
389{
390 int16_t a0 = (int16_t)(a & 0xffff);
391 int16_t a1 = (int16_t)(a >> 16);
392 int16_t b0 = (int16_t)(b & 0xffff);
393 int16_t b1 = (int16_t)(b >> 16);
394 return acc + ((int64_t)a0 * b0) + ((int64_t)a1 * b1);
395}
396
397static inline int32_t arm_smlawb(int32_t acc, int32_t a, int32_t b)
398{
399 return acc + (int32_t)(((int64_t)a * (int16_t)(b & 0xffff)) >> 16);
400}
401
402static inline int32_t arm_smlawt(int32_t acc, int32_t a, int32_t b)
403{
404 return acc + (int32_t)(((int64_t)a * (int16_t)(b >> 16)) >> 16);
405}
406
407static inline int32_t arm_sxth(int32_t val)
408{
409 return (int32_t)(int16_t)(val & 0xffff);
410}
411
412static inline uint32_t arm_clz(uint32_t val)
413{
414 return (uint32_t)__builtin_clz(val);
415}
416
417#endif /* defined(__arm__) || defined(__thumb__) || defined(CONFIG_ARM) */
418
419#endif /* __SOF_AUDIO_ARM_SIMD_H__ */
static int64_t arm_smlald(uint32_t a, uint32_t b, int64_t acc)
Definition arm_simd.h:388
static int32_t arm_qsub(int32_t a, int32_t b)
Definition arm_simd.h:349
static int32_t arm_smultt(int32_t a, int32_t b)
Definition arm_simd.h:359
static uint32_t arm_qadd16(uint32_t a, uint32_t b)
Definition arm_simd.h:322
static int32_t arm_smlawt(int32_t acc, int32_t a, int32_t b)
Definition arm_simd.h:402
static int32_t arm_smlad(uint32_t a, uint32_t b, int32_t acc)
Definition arm_simd.h:379
static uint32_t arm_pkhbt(uint32_t a, uint32_t b, uint32_t lsl_shift)
Definition arm_simd.h:374
static uint32_t arm_qsub16(uint32_t a, uint32_t b)
Definition arm_simd.h:333
static int32_t arm_smulbb(int32_t a, int32_t b)
Definition arm_simd.h:354
static int64_t arm_smlal(int64_t acc, int32_t a, int32_t b)
Definition arm_simd.h:369
static int32_t arm_sat_q31(int64_t val)
Definition arm_simd.h:315
static int32_t arm_qadd(int32_t a, int32_t b)
Definition arm_simd.h:344
static int32_t arm_smlawb(int32_t acc, int32_t a, int32_t b)
Definition arm_simd.h:397
static int16_t arm_sat_s16(int32_t val)
Definition arm_simd.h:301
static int32_t arm_sxth(int32_t val)
Definition arm_simd.h:407
static uint32_t arm_clz(uint32_t val)
Definition arm_simd.h:412
static int32_t arm_sat_s24(int32_t val)
Definition arm_simd.h:308
static int32_t arm_smmulr(int32_t a, int32_t b)
Definition arm_simd.h:364