1
0
mirror of https://github.com/gnss-sdr/gnss-sdr synced 2025-11-07 02:33:03 +00:00

Implement RVV 32fc to 8i conversion

Implement converting a vector of 32-bit floating point
complex numbers into a vector of 8-bit integer complex numbers
using RVV C intrinsics in `volk_gnsssdr_32fc_convert_8i_rvv`.

Required a lot of debugging, with attempts ranging from
saturation to not doing a narrowing conversion to using
the specification-recommended "round-to-odd" conversion.

In end, the problem was that the generic
implementation, with no notice in the function documentation
at all, actually multiplies the floating-point number by `INT8_MAX`
before converting.

Signed-off-by: Marcus Alagar <mvala079@gmail.com>
This commit is contained in:
Marcus Alagar
2025-08-10 22:43:35 -05:00
parent bdddb5dd68
commit f07af19b7e

View File

@@ -452,4 +452,58 @@ static inline void volk_gnsssdr_32fc_convert_8ic_neon(lv_8sc_t* outputVector, co
#endif /* LV_HAVE_NEON */
#ifdef LV_HAVE_RVV
#include <riscv_vector.h>
static inline void volk_gnsssdr_32fc_convert_8ic_rvv(lv_8sc_t* outputVector, const lv_32fc_t* inputVector, unsigned int num_points)
{
size_t n = num_points;
// Initialize pointers to keep track as stripmine
// Assuming `signed char` is intended, as `char`'s
// signedness is implementation-based
signed char* outPtr = (signed char*) outputVector;
const float* inPtr = (const float*) inputVector;
for (size_t vl; n > 0; n -= vl, outPtr += vl * 2, inPtr += vl * 2)
{
// Record how many elements will actually be processed
vl = __riscv_vsetvl_e32m4(n);
// Load inReal[0..vl), inImag[0..vl)
vfloat32m4x2_t inVal = __riscv_vlseg2e32_v_f32m4x2(inPtr, vl);
vfloat32m4_t inRealVal = __riscv_vget_v_f32m4x2_f32m4(inVal, 0);
vfloat32m4_t inImagVal = __riscv_vget_v_f32m4x2_f32m4(inVal, 1);
// For some reason, generic implementation
// multiplies float by `INT8_MAX` before converting
// tmpReal[i], tmpImag[i] *= INT8_MAX
vfloat32m4_t tmp32RealVal = __riscv_vfmul_vf_f32m4(inRealVal, (float) 127, vl);
vfloat32m4_t tmp32ImagVal = __riscv_vfmul_vf_f32m4(inImagVal, (float) 127, vl);
// outReal[i] = (signed char) tmpReal[i]
vint16m2_t tmp16RealVal = __riscv_vfncvt_x_f_w_i16m2(tmp32RealVal, vl);
vint8m1_t outRealVal = __riscv_vncvt_x_x_w_i8m1(tmp16RealVal, vl);
// outImag[i] = (signed char) tmpImag[i]
vint16m2_t tmp16ImagVal = __riscv_vfncvt_x_f_w_i16m2(tmp32ImagVal, vl);
vint8m1_t outImagVal = __riscv_vncvt_x_x_w_i8m1(tmp16ImagVal, vl);
// Store outReal[0..vl), outImag[0..vl)
vint8m1x2_t outVal = __riscv_vset_v_i8m1_i8m1x2(
__riscv_vundefined_i8m1x2(), 0, outRealVal
);
outVal = __riscv_vset_v_i8m1_i8m1x2(outVal, 1, outImagVal);
__riscv_vsseg2e8_v_i8m1x2(outPtr, outVal, vl);
// In looping, decrement the number of
// elements left and increment the pointers
// by the number of elements processed,
// accounting how the `vl` complex numbers
// are each stored as two numbers of their
// corresponding size.
}
}
#endif /* LV_HAVE_RVV */
#endif /* INCLUDED_volk_gnsssdr_32fc_convert_8ic_H */