From d7ea319b591df1562ddddefbcc09e1fac7a9b3bb Mon Sep 17 00:00:00 2001 From: Eric Mittag Date: Fri, 30 Jan 2026 16:32:17 +0100 Subject: [PATCH] cmac0_prec128()/rotate_cpx_vector(): Add ARMv8.0-A fallback The current code, on ARM, uses ARMv8.1-A RDM instructions. Add a fallback for processors that do not have this instruction set. --- openair1/PHY/MODULATION/nr_modulation.c | 18 +++++++++++++++++- openair1/PHY/TOOLS/tools_defs.h | 17 ++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/openair1/PHY/MODULATION/nr_modulation.c b/openair1/PHY/MODULATION/nr_modulation.c index eb4dc50bc9..562c43895b 100644 --- a/openair1/PHY/MODULATION/nr_modulation.c +++ b/openair1/PHY/MODULATION/nr_modulation.c @@ -760,6 +760,8 @@ static inline __attribute__((always_inline)) int16x8_t cmac0_prec128(int16x8_t x // int16x8_t xr = vuzp1q_s16(x, x); // even lanes int16x8_t xi = vuzp2q_s16(x, x); // odd lanes +#ifdef __ARM_FEATURE_QRDMX + // ARMv8.1-A: Use RDM instructions // real = ar*br - ai*bi (Q15 scaling via high-half doubling muls) int16x8_t real = vqdmulhq_s16(xr, wr); // ≈ round((2*xr*wr)/2^16) real = vqrdmlshq_s16(real, xi, wi); // real -= round((2*xi*wi)/2^16) @@ -767,7 +769,21 @@ static inline __attribute__((always_inline)) int16x8_t cmac0_prec128(int16x8_t x // imag = ar*bi + ai*br int16x8_t imag = vqdmulhq_s16(xr, wi); imag = vqrdmlahq_s16(imag, xi, wr); // imag += round((2*xi*wr)/2^16) - // +#else + // ARMv8.0-A fallback: Use standard 32-bit multiply + int32x4_t real_lo = vmull_s16(vget_low_s16(xr), vget_low_s16(wr)); + int32x4_t real_hi = vmull_s16(vget_high_s16(xr), vget_high_s16(wr)); + real_lo = vmlsl_s16(real_lo, vget_low_s16(xi), vget_low_s16(wi)); + real_hi = vmlsl_s16(real_hi, vget_high_s16(xi), vget_high_s16(wi)); + + int32x4_t imag_lo = vmull_s16(vget_low_s16(xr), vget_low_s16(wi)); + int32x4_t imag_hi = vmull_s16(vget_high_s16(xr), vget_high_s16(wi)); + imag_lo = vmlal_s16(imag_lo, vget_low_s16(xi), vget_low_s16(wr)); + imag_hi = vmlal_s16(imag_hi, vget_high_s16(xi), vget_high_s16(wr)); + + int16x8_t real = vcombine_s16(vqrshrn_n_s32(real_lo, 15), vqrshrn_n_s32(real_hi, 15)); + int16x8_t imag = vcombine_s16(vqrshrn_n_s32(imag_lo, 15), vqrshrn_n_s32(imag_hi, 15)); +#endif // Re-interleave [real, imag] int16x8x2_t produ = vzipq_s16(real, imag); return produ.val[0]; diff --git a/openair1/PHY/TOOLS/tools_defs.h b/openair1/PHY/TOOLS/tools_defs.h index 36fc496545..40f6ca46f6 100644 --- a/openair1/PHY/TOOLS/tools_defs.h +++ b/openair1/PHY/TOOLS/tools_defs.h @@ -816,7 +816,8 @@ static inline void rotate_cpx_vector(const c16_t *const x, const c16_t *const al // Split interleaved -> separate real/imag int16x8_t br = vuzp1q_s16(x_128[i], x_128[i]); int16x8_t bi = vuzp2q_s16(x_128[i], x_128[i]); - +#ifdef __ARM_FEATURE_QRDMX + // ARMv8.1-A: Use RDM instructions (rounding doubling multiply) // Start with the two “diagonal” products using high-half, doubling, sat: // x = round( (2*ar*br) / 2^16 ), y = round( (2*ar*bi) / 2^16 ) int16x8_t real = vqdmulhq_s16(ar, br); @@ -827,7 +828,21 @@ static inline void rotate_cpx_vector(const c16_t *const x, const c16_t *const al // imag += round( (2*ai*br) / 2^16 ) imag = vqrdmlahq_s16(imag, ai, br); +#else + // ARMv8.0-A fallback: Use standard 32-bit multiply + int32x4_t real_lo = vmull_s16(vget_low_s16(ar), vget_low_s16(br)); + int32x4_t real_hi = vmull_s16(vget_high_s16(ar), vget_high_s16(br)); + real_lo = vmlsl_s16(real_lo, vget_low_s16(ai), vget_low_s16(bi)); + real_hi = vmlsl_s16(real_hi, vget_high_s16(ai), vget_high_s16(bi)); + int32x4_t imag_lo = vmull_s16(vget_low_s16(ar), vget_low_s16(bi)); + int32x4_t imag_hi = vmull_s16(vget_high_s16(ar), vget_high_s16(bi)); + imag_lo = vmlal_s16(imag_lo, vget_low_s16(ai), vget_low_s16(br)); + imag_hi = vmlal_s16(imag_hi, vget_high_s16(ai), vget_high_s16(br)); + + int16x8_t real = vcombine_s16(vqrshrn_n_s32(real_lo, 15), vqrshrn_n_s32(real_hi, 15)); + int16x8_t imag = vcombine_s16(vqrshrn_n_s32(imag_lo, 15), vqrshrn_n_s32(imag_hi, 15)); +#endif // Re-interleave [real, imag] int16x8x2_t z = vzipq_s16(real, imag);