From 7db4dfec732d96b39a40b277c6de6580697a2229 Mon Sep 17 00:00:00 2001 From: Rupanjali Date: Wed, 3 Jun 2026 12:26:26 -0400 Subject: [PATCH] Created common ML LLR and MMSE for 2 layers functions being shared by both gNB and UE. Disabled ML based LLR due to lareg compute time Signed-off-by: Rupanjali --- CMakeLists.txt | 1 - .../PHY/NR_TRANSPORT/nr_transport_proto.h | 53 - .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 661 +--- .../NR_TRANSPORT/nr_ulsch_llr_computation.c | 1796 ---------- .../NR_UE_TRANSPORT/nr_dlsch_demodulation.c | 92 +- .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 3 +- .../PHY/nr_phy_common/inc/nr_phy_common.h | 53 + .../PHY/nr_phy_common/src/nr_phy_common.c | 3057 +++++++++++++++++ openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 7 +- 9 files changed, 3215 insertions(+), 2508 deletions(-) delete mode 100644 openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c diff --git a/CMakeLists.txt b/CMakeLists.txt index ff5d4254ae..b1a85819a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -924,7 +924,6 @@ set(PHY_SRC_UE ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_ulsch.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_sch_dmrs.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_prach.c - ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c ${OPENAIR1_DIR}/PHY/NR_REFSIG/ul_ref_seq_nr.c ${OPENAIR1_DIR}/PHY/NR_REFSIG/nr_dmrs_rx.c diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 89b0eb044e..631677c1c1 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -106,62 +106,9 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, */ void nr_idft(int32_t *z, uint32_t Msc_PUSCH); -void nr_ulsch_qpsk_qpsk(c16_t *stream0_in, - c16_t *stream1_in, - c16_t *stream0_out, - c16_t *rho01, - uint32_t length); - -void nr_ulsch_qam16_qam16(c16_t *stream0_in, - c16_t *stream1_in, - c16_t *ch_mag, - c16_t *ch_mag_i, - c16_t *stream0_out, - c16_t *rho01, - uint32_t length); - -void nr_ulsch_qam64_qam64(c16_t *stream0_in, - c16_t *stream1_in, - c16_t *ch_mag, - c16_t *ch_mag_i, - c16_t *stream0_out, - c16_t *rho01, - uint32_t length); - -/** \brief This function computes the log-likelihood ratios for 4, 16, and 64 QAM - @param rxdataF_comp Compensated channel output - @param ul_ch_mag uplink channel magnitude multiplied by the 1st amplitude threshold in QAM 64 - @param ul_ch_magb uplink channel magnitude multiplied by the 2bd amplitude threshold in QAM 64 - @param ulsch_llr llr output - @param nb_re number of REs for this allocation - @param symbol OFDM symbol index in sub-frame - @param mod_order modulation order -*/ -void nr_ulsch_compute_llr(c16_t *rxdataF_comp, - c16_t *ul_ch_mag, - c16_t *ul_ch_magb, - c16_t *ul_ch_magc, - int16_t *ulsch_llr, - uint32_t nb_re, - uint8_t symbol, - uint8_t mod_order); - void reset_active_stats(PHY_VARS_gNB *gNB, int frame); void reset_active_ulsch(PHY_VARS_gNB *gNB, int frame); -void nr_ulsch_compute_ML_llr(c16_t *rxdataF_comp0, - c16_t *rxdataF_comp1, - c16_t *ul_ch_mag0, - c16_t *ul_ch_mag1, - int16_t *llr_layers0, - int16_t *llr_layers1, - c16_t *rho0, - c16_t *rho1, - uint32_t nb_re, - uint8_t mod_order); - -void nr_ulsch_shift_llr(int16_t **llr_layers, uint32_t nb_re, uint32_t rxdataF_ext_offset, uint8_t mod_order, int shift); - void nr_fill_ulsch(PHY_VARS_gNB *gNB, int frame, int slot, diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 121825b78c..56a150d028 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -174,604 +174,6 @@ static int get_nb_re_pusch (NR_DL_FRAME_PARMS *frame_parms, const nfapi_nr_pusch return (rel15_ul->rb_size * NR_NB_SC_PER_RB); } -// Zero Forcing Rx function: nr_det_HhH() -static void nr_ulsch_det_HhH(c16_t *after_mf_00, // a - c16_t *after_mf_01, // b - c16_t *after_mf_10, // c - c16_t *after_mf_11, // d - uint32_t *det_fin, // 1/ad-bc - unsigned short nb_rb) -{ - simde__m128i *after_mf_00_128,*after_mf_01_128, *after_mf_10_128, *after_mf_11_128, ad_re_128, bc_re_128; //ad_im_128, bc_im_128; - simde__m128i *det_fin_128, det_re_128; //det_im_128, tmp_det0, tmp_det1; - - after_mf_00_128 = (simde__m128i *)after_mf_00; - after_mf_01_128 = (simde__m128i *)after_mf_01; - after_mf_10_128 = (simde__m128i *)after_mf_10; - after_mf_11_128 = (simde__m128i *)after_mf_11; - - det_fin_128 = (simde__m128i *)det_fin; - - for (unsigned short rb=0; rb<3*nb_rb; rb++) { - - //complex multiplication (I_a+jQ_a)(I_d+jQ_d) = (I_aI_d - Q_aQ_d) + j(Q_aI_d + I_aQ_d) - //The imag part is often zero, we compute only the real part - ad_re_128 = simde_mm_madd_epi16(oai_mm_conj(after_mf_00_128[0]),after_mf_11_128[0]); //Re: I_a0*I_d0 - Q_a1*Q_d1 - //ad_im_128 = simde_mm_madd_epi16(oai_mm_swap(after_mf_00_128[0]),after_mf_11_128[0]);//Im: (Q_aI_d + I_aQ_d) - - //complex multiplication (I_b+jQ_b)(I_c+jQ_c) = (I_bI_c - Q_bQ_c) + j(Q_bI_c + I_bQ_c) - //The imag part is often zero, we compute only the real part - bc_re_128 = simde_mm_madd_epi16(oai_mm_conj(after_mf_01_128[0]),after_mf_10_128[0]); //Re: I_b0*I_c0 - Q_b1*Q_c1 - //bc_im_128 = simde_mm_madd_epi16(oai_mm_swap(after_mf_01_128[0]),after_mf_10_128[0]);//Im: (Q_bI_c + I_bQ_c) - - det_re_128 = simde_mm_sub_epi32(ad_re_128, bc_re_128); - //det_im_128 = simde_mm_sub_epi32(ad_im_128, bc_im_128); - - //det in Q30 format - det_fin_128[0] = simde_mm_abs_epi32(det_re_128); - - -#ifdef DEBUG_DLSCH_DEMOD - printf("\n Computing det_HhH_inv \n"); - //print_ints("det_re_128:",(int32_t*)&det_re_128); - //print_ints("det_im_128:",(int32_t*)&det_im_128); - print_ints("det_fin_128:",(int32_t*)&det_fin_128[0]); -#endif - det_fin_128+=1; - after_mf_00_128+=1; - after_mf_01_128+=1; - after_mf_10_128+=1; - after_mf_11_128+=1; - } -} - -/* Zero Forcing Rx function: nr_conjch0_mult_ch1() - * - * - * */ -// TODO: This function is just a wrapper, can be removed. -static void nr_ulsch_conjch0_mult_ch1(c16_t *ch0, c16_t *ch1, c16_t *ch0conj_ch1, unsigned short nb_rb, unsigned char output_shift0) -{ - //This function is used to compute multiplications in H_hermitian * H matrix - mult_cpx_conj_vector(ch0, ch1, ch0conj_ch1, 12 * nb_rb, output_shift0); -} - -static simde__m128i nr_ulsch_comp_muli_sum(simde__m128i input_x, - simde__m128i input_y, - simde__m128i input_w, - simde__m128i input_z, - simde__m128i det) -{ - - // complex multiplication (x_re + jx_im)*(y_re + jy_im) = (x_re*y_re - x_im*y_im) + j(x_im*y_re + x_re*y_im) - // complex multiplication (w_re + jw_im)*(z_re + jz_im) = (w_re*z_re - w_im*z_im) + j(w_im*z_re + w_re*z_im) - // the real part - simde__m128i xy_re_128 = simde_mm_madd_epi16(oai_mm_conj(input_x), input_y); //Re: (x_re*y_re - x_im*y_im) - simde__m128i wz_re_128 = simde_mm_madd_epi16(oai_mm_conj(input_w), input_z); //Re: (w_re*z_re - w_im*z_im) - xy_re_128 = simde_mm_sub_epi32(xy_re_128, wz_re_128); - - // the imag part - simde__m128i xy_im_128 = simde_mm_madd_epi16(oai_mm_swap(input_x), input_y); //Im: (x_im*y_re + x_re*y_im) - simde__m128i wz_im_128 = simde_mm_madd_epi16(oai_mm_swap(input_w), input_z); //Im: (w_im*z_re + w_re*z_im) - xy_im_128 = simde_mm_sub_epi32(xy_im_128, wz_im_128); - - //print_ints("rx_re:",(int32_t*)&xy_re_128[0]); - //print_ints("rx_Img:",(int32_t*)&xy_im_128[0]); - //divide by matrix det and convert back to Q15 before packing - uint64_t sum_det = 0; - for (int k = 0; k < 4; k++) { - sum_det += (((uint32_t *)&det)[k]); - } - // Add bias to reduce rounding error - sum_det = (sum_det + 2) >> 2; - - int b = log2_approx(sum_det) - 8; - if (b > 0) { - xy_re_128 = simde_mm_srai_epi32(xy_re_128, b); - xy_im_128 = simde_mm_srai_epi32(xy_im_128, b); - } else { - xy_re_128 = simde_mm_slli_epi32(xy_re_128, -b); - xy_im_128 = simde_mm_slli_epi32(xy_im_128, -b); - } - - simde__m128i output = oai_mm_pack(xy_re_128, xy_im_128); - - return(output); -} - -/* Zero Forcing Rx function: nr_construct_HhH_elements() - * - * - * */ -static void nr_ulsch_construct_HhH_elements(c16_t *conjch00_ch00, - c16_t *conjch01_ch01, - c16_t *conjch11_ch11, - c16_t *conjch10_ch10, // - c16_t *conjch20_ch20, - c16_t *conjch21_ch21, - c16_t *conjch30_ch30, - c16_t *conjch31_ch31, - c16_t *conjch00_ch01, // 00_01 - c16_t *conjch01_ch00, // 01_00 - c16_t *conjch10_ch11, // 10_11 - c16_t *conjch11_ch10, // 11_10 - c16_t *conjch20_ch21, - c16_t *conjch21_ch20, - c16_t *conjch30_ch31, - c16_t *conjch31_ch30, - c16_t *after_mf_00, - c16_t *after_mf_01, - c16_t *after_mf_10, - c16_t *after_mf_11, - unsigned short nb_rb) -{ - //This function is used to construct the (H_hermitian * H matrix) matrix elements - simde__m128i *conjch00_ch00_128 = (simde__m128i *)conjch00_ch00; - simde__m128i *conjch01_ch01_128 = (simde__m128i *)conjch01_ch01; - simde__m128i *conjch11_ch11_128 = (simde__m128i *)conjch11_ch11; - simde__m128i *conjch10_ch10_128 = (simde__m128i *)conjch10_ch10; - - simde__m128i *conjch20_ch20_128 = (simde__m128i *)conjch20_ch20; - simde__m128i *conjch21_ch21_128 = (simde__m128i *)conjch21_ch21; - simde__m128i *conjch30_ch30_128 = (simde__m128i *)conjch30_ch30; - simde__m128i *conjch31_ch31_128 = (simde__m128i *)conjch31_ch31; - - simde__m128i *conjch00_ch01_128 = (simde__m128i *)conjch00_ch01; - simde__m128i *conjch01_ch00_128 = (simde__m128i *)conjch01_ch00; - simde__m128i *conjch10_ch11_128 = (simde__m128i *)conjch10_ch11; - simde__m128i *conjch11_ch10_128 = (simde__m128i *)conjch11_ch10; - - simde__m128i *conjch20_ch21_128 = (simde__m128i *)conjch20_ch21; - simde__m128i *conjch21_ch20_128 = (simde__m128i *)conjch21_ch20; - simde__m128i *conjch30_ch31_128 = (simde__m128i *)conjch30_ch31; - simde__m128i *conjch31_ch30_128 = (simde__m128i *)conjch31_ch30; - - simde__m128i *after_mf_00_128 = (simde__m128i *)after_mf_00; - simde__m128i *after_mf_01_128 = (simde__m128i *)after_mf_01; - simde__m128i *after_mf_10_128 = (simde__m128i *)after_mf_10; - simde__m128i *after_mf_11_128 = (simde__m128i *)after_mf_11; - - for (unsigned short rb=0; rb<3*nb_rb; rb++) { - - after_mf_00_128[0] = simde_mm_adds_epi16(conjch00_ch00_128[0], conjch10_ch10_128[0]); //00_00 + 10_10 - if (conjch20_ch20 != NULL) after_mf_00_128[0] = simde_mm_adds_epi16(after_mf_00_128[0], conjch20_ch20_128[0]); - if (conjch30_ch30 != NULL) after_mf_00_128[0] = simde_mm_adds_epi16(after_mf_00_128[0], conjch30_ch30_128[0]); - - after_mf_11_128[0] = simde_mm_adds_epi16(conjch01_ch01_128[0], conjch11_ch11_128[0]); //01_01 + 11_11 - if (conjch21_ch21 != NULL) after_mf_11_128[0] = simde_mm_adds_epi16(after_mf_11_128[0], conjch21_ch21_128[0]); - if (conjch31_ch31 != NULL) after_mf_11_128[0] = simde_mm_adds_epi16(after_mf_11_128[0], conjch31_ch31_128[0]); - - after_mf_01_128[0] = simde_mm_adds_epi16(conjch00_ch01_128[0], conjch10_ch11_128[0]); //00_01 + 10_11 - if (conjch20_ch21 != NULL) after_mf_01_128[0] = simde_mm_adds_epi16(after_mf_01_128[0], conjch20_ch21_128[0]); - if (conjch30_ch31 != NULL) after_mf_01_128[0] = simde_mm_adds_epi16(after_mf_01_128[0], conjch30_ch31_128[0]); - - after_mf_10_128[0] = simde_mm_adds_epi16(conjch01_ch00_128[0], conjch11_ch10_128[0]); //01_00 + 11_10 - if (conjch21_ch20 != NULL) after_mf_10_128[0] = simde_mm_adds_epi16(after_mf_10_128[0], conjch21_ch20_128[0]); - if (conjch31_ch30 != NULL) after_mf_10_128[0] = simde_mm_adds_epi16(after_mf_10_128[0], conjch31_ch30_128[0]); - -#ifdef DEBUG_DLSCH_DEMOD - if ((rb<=30)) - { - printf(" \n construct_HhH_elements \n"); - print_shorts("after_mf_00_128:",(int16_t*)&after_mf_00_128[0]); - print_shorts("after_mf_01_128:",(int16_t*)&after_mf_01_128[0]); - print_shorts("after_mf_10_128:",(int16_t*)&after_mf_10_128[0]); - print_shorts("after_mf_11_128:",(int16_t*)&after_mf_11_128[0]); - } -#endif - conjch00_ch00_128+=1; - conjch10_ch10_128+=1; - conjch01_ch01_128+=1; - conjch11_ch11_128+=1; - - if (conjch20_ch20 != NULL) conjch20_ch20_128+=1; - if (conjch21_ch21 != NULL) conjch21_ch21_128+=1; - if (conjch30_ch30 != NULL) conjch30_ch30_128+=1; - if (conjch31_ch31 != NULL) conjch31_ch31_128+=1; - - conjch00_ch01_128+=1; - conjch01_ch00_128+=1; - conjch10_ch11_128+=1; - conjch11_ch10_128+=1; - - if (conjch20_ch21 != NULL) conjch20_ch21_128+=1; - if (conjch21_ch20 != NULL) conjch21_ch20_128+=1; - if (conjch30_ch31 != NULL) conjch30_ch31_128+=1; - if (conjch31_ch30 != NULL) conjch31_ch30_128+=1; - - after_mf_00_128 += 1; - after_mf_01_128 += 1; - after_mf_10_128 += 1; - after_mf_11_128 += 1; - } -} - -// MMSE Rx function: nr_ulsch_mmse_2layers() -static uint8_t nr_ulsch_mmse_2layers(c16_t **rxdataF_comp, - uint32_t buffer_length, - int nb_rx_ant, - c16_t ul_ch_mag[][buffer_length], - c16_t ul_ch_magb[][buffer_length], - c16_t ul_ch_magc[][buffer_length], - c16_t ul_ch_estimates_ext[][nb_rx_ant][buffer_length], - unsigned short nb_rb, - unsigned char mod_order, - int shift, - unsigned char symbol, - int length, - uint32_t noise_var) -{ - uint32_t nb_rb_0 = length/12 + ((length%12)?1:0); - - /* we need at least alignment to 16 bytes, let's put 32 to be sure - * (maybe not necessary but doesn't hurt) - */ - c16_t conjch00_ch01[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch01_ch00[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch10_ch11[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch11_ch10[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch00_ch00[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch01_ch01[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch10_ch10[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch11_ch11[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch20_ch20[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch21_ch21[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch30_ch30[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch31_ch31[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch20_ch21[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch30_ch31[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch21_ch20[12 * nb_rb] __attribute__((aligned(32))); - c16_t conjch31_ch30[12 * nb_rb] __attribute__((aligned(32))); - - c16_t af_mf_00[12 * nb_rb] __attribute__((aligned(32))); - c16_t af_mf_01[12 * nb_rb] __attribute__((aligned(32))); - c16_t af_mf_10[12 * nb_rb] __attribute__((aligned(32))); - c16_t af_mf_11[12 * nb_rb] __attribute__((aligned(32))); - uint32_t determ_fin[12*nb_rb] __attribute__((aligned(32))); - - c16_t *ch00, *ch01, *ch10, *ch11; - c16_t *ch20, *ch30, *ch21, *ch31; - switch (nb_rx_ant) { - case 2:// - ch00 = ul_ch_estimates_ext[0][0]; - ch01 = ul_ch_estimates_ext[1][0]; - ch10 = ul_ch_estimates_ext[0][1]; - ch11 = ul_ch_estimates_ext[1][1]; - ch20 = NULL; - ch21 = NULL; - ch30 = NULL; - ch31 = NULL; - break; - - case 4:// - ch00 = ul_ch_estimates_ext[0][0]; - ch01 = ul_ch_estimates_ext[1][0]; - ch10 = ul_ch_estimates_ext[0][1]; - ch11 = ul_ch_estimates_ext[1][1]; - ch20 = ul_ch_estimates_ext[0][2]; - ch21 = ul_ch_estimates_ext[1][2]; - ch30 = ul_ch_estimates_ext[0][3]; - ch31 = ul_ch_estimates_ext[1][3]; - break; - - default: - return -1; - break; - } - - /* 1- Compute the rx channel matrix after compensation: (1/2^log2_max)x(H_herm x H) - * for n_rx = 2 - * |conj_H_00 conj_H_10| | H_00 H_01| |(conj_H_00xH_00+conj_H_10xH_10) (conj_H_00xH_01+conj_H_10xH_11)| - * | | x | | = | | - * |conj_H_01 conj_H_11| | H_10 H_11| |(conj_H_01xH_00+conj_H_11xH_10) (conj_H_01xH_01+conj_H_11xH_11)| - * - */ - - if (nb_rx_ant >= 2) { - // (1/2^log2_maxh)*conj_H_00xH_00: (1/(64*2))conjH_00*H_00*2^15 - nr_ulsch_conjch0_mult_ch1(ch00, - ch00, - conjch00_ch00, - nb_rb_0, - shift); - // (1/2^log2_maxh)*conj_H_10xH_10: (1/(64*2))conjH_10*H_10*2^15 - nr_ulsch_conjch0_mult_ch1(ch10, - ch10, - conjch10_ch10, - nb_rb_0, - shift); - // conj_H_00xH_01 - nr_ulsch_conjch0_mult_ch1(ch00, - ch01, - conjch00_ch01, - nb_rb_0, - shift); // this shift is equal to the channel level log2_maxh - // conj_H_10xH_11 - nr_ulsch_conjch0_mult_ch1(ch10, - ch11, - conjch10_ch11, - nb_rb_0, - shift); - // conj_H_01xH_01 - nr_ulsch_conjch0_mult_ch1(ch01, - ch01, - conjch01_ch01, - nb_rb_0, - shift); - // conj_H_11xH_11 - nr_ulsch_conjch0_mult_ch1(ch11, - ch11, - conjch11_ch11, - nb_rb_0, - shift); - // conj_H_01xH_00 - nr_ulsch_conjch0_mult_ch1(ch01, - ch00, - conjch01_ch00, - nb_rb_0, - shift); - // conj_H_11xH_10 - nr_ulsch_conjch0_mult_ch1(ch11, - ch10, - conjch11_ch10, - nb_rb_0, - shift); - } - if (nb_rx_ant == 4) { - // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2*16))conjH_20*H_20*2^15 - nr_ulsch_conjch0_mult_ch1(ch20, - ch20, - conjch20_ch20, - nb_rb_0, - shift); - - // (1/2^log2_maxh)*conj_H_30xH_30: (1/(64*2*4))conjH_30*H_30*2^15 - nr_ulsch_conjch0_mult_ch1(ch30, - ch30, - conjch30_ch30, - nb_rb_0, - shift); - - // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2))conjH_20*H_20*2^15 - nr_ulsch_conjch0_mult_ch1(ch20, - ch21, - conjch20_ch21, - nb_rb_0, - shift); - - nr_ulsch_conjch0_mult_ch1(ch30, - ch31, - conjch30_ch31, - nb_rb_0, - shift); - - nr_ulsch_conjch0_mult_ch1(ch21, - ch21, - conjch21_ch21, - nb_rb_0, - shift); - - nr_ulsch_conjch0_mult_ch1(ch31, - ch31, - conjch31_ch31, - nb_rb_0, - shift); - - // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2))conjH_20*H_20*2^15 - nr_ulsch_conjch0_mult_ch1(ch21, - ch20, - conjch21_ch20, - nb_rb_0, - shift); - - nr_ulsch_conjch0_mult_ch1(ch31, - ch30, - conjch31_ch30, - nb_rb_0, - shift); - - nr_ulsch_construct_HhH_elements(conjch00_ch00, - conjch01_ch01, - conjch11_ch11, - conjch10_ch10,// - conjch20_ch20, - conjch21_ch21, - conjch30_ch30, - conjch31_ch31, - conjch00_ch01, - conjch01_ch00, - conjch10_ch11, - conjch11_ch10,// - conjch20_ch21, - conjch21_ch20, - conjch30_ch31, - conjch31_ch30, - af_mf_00, - af_mf_01, - af_mf_10, - af_mf_11, - nb_rb_0); - } - if (nb_rx_ant == 2) { - nr_ulsch_construct_HhH_elements(conjch00_ch00, - conjch01_ch01, - conjch11_ch11, - conjch10_ch10,// - NULL, - NULL, - NULL, - NULL, - conjch00_ch01, - conjch01_ch00, - conjch10_ch11, - conjch11_ch10,// - NULL, - NULL, - NULL, - NULL, - af_mf_00, - af_mf_01, - af_mf_10, - af_mf_11, - nb_rb_0); - } - - // Add noise_var such that: H^h * H + noise_var * I - if (noise_var != 0) { - simde__m128i nvar_128i = simde_mm_set1_epi32(noise_var >> shift); - simde__m128i *af_mf_00_128i = (simde__m128i *)af_mf_00; - simde__m128i *af_mf_11_128i = (simde__m128i *)af_mf_11; - for (int k = 0; k < 3 * nb_rb_0; k++) { - af_mf_00_128i[0] = simde_mm_add_epi32(af_mf_00_128i[0], nvar_128i); - af_mf_11_128i[0] = simde_mm_add_epi32(af_mf_11_128i[0], nvar_128i); - af_mf_00_128i++; - af_mf_11_128i++; - } - } - - //det_HhH = ad -bc - nr_ulsch_det_HhH(af_mf_00,//a - af_mf_01,//b - af_mf_10,//c - af_mf_11,//d - determ_fin, - nb_rb_0); - /* 2- Compute the channel matrix inversion ********************************** - * - * |(conj_H_00xH_00+conj_H_10xH_10) (conj_H_00xH_01+conj_H_10xH_11)| - * A= | | - * |(conj_H_01xH_00+conj_H_11xH_10) (conj_H_01xH_01+conj_H_11xH_11)| - * - * - * - *inv(A) =(1/det)*[d -b - * -c a] - * - * - **************************************************************************/ - simde__m128i *ul_ch_mag128_0 = NULL, *ul_ch_mag128b_0 = NULL, *ul_ch_mag128c_0 = NULL; // Layer 0 - simde__m128i *ul_ch_mag128_1 = NULL, *ul_ch_mag128b_1 = NULL, *ul_ch_mag128c_1 = NULL; // Layer 1 - simde__m128i mmtmpD0, mmtmpD1, mmtmpD2, mmtmpD3; - simde__m128i QAM_amp128 = {0}, QAM_amp128b = {0}, QAM_amp128c = {0}; - - simde__m128i *determ_fin_128 = (simde__m128i *)&determ_fin[0]; - - simde__m128i *after_mf_a_128 = (simde__m128i *)af_mf_00; - simde__m128i *after_mf_b_128 = (simde__m128i *)af_mf_01; - simde__m128i *after_mf_c_128 = (simde__m128i *)af_mf_10; - simde__m128i *after_mf_d_128 = (simde__m128i *)af_mf_11; - - simde__m128i *rxdataF_comp128_0 = (simde__m128i *)&rxdataF_comp[0][symbol * buffer_length]; - simde__m128i *rxdataF_comp128_1 = (simde__m128i *)&rxdataF_comp[nb_rx_ant][symbol * buffer_length]; - - if (mod_order > 2) { - if (mod_order == 4) { - QAM_amp128 = simde_mm_set1_epi16(QAM16_n1); // 2/sqrt(10) - QAM_amp128b = simde_mm_setzero_si128(); - QAM_amp128c = simde_mm_setzero_si128(); - } else if (mod_order == 6) { - QAM_amp128 = simde_mm_set1_epi16(QAM64_n1); // 4/sqrt{42} - QAM_amp128b = simde_mm_set1_epi16(QAM64_n2); // 2/sqrt{42} - QAM_amp128c = simde_mm_setzero_si128(); - } else if (mod_order == 8) { - QAM_amp128 = simde_mm_set1_epi16(QAM256_n1); - QAM_amp128b = simde_mm_set1_epi16(QAM256_n2); - QAM_amp128c = simde_mm_set1_epi16(QAM256_n3); - } - ul_ch_mag128_0 = (simde__m128i *)&ul_ch_mag[0]; - ul_ch_mag128b_0 = (simde__m128i *)&ul_ch_magb[0]; - ul_ch_mag128c_0 = (simde__m128i *)&ul_ch_magc[0]; - ul_ch_mag128_1 = (simde__m128i *)&ul_ch_mag[1]; - ul_ch_mag128b_1 = (simde__m128i *)&ul_ch_magb[1]; - ul_ch_mag128c_1 = (simde__m128i *)&ul_ch_magc[1]; - } - - for (int rb = 0; rb < 3 * nb_rb_0; rb++) { - - // Magnitude computation - if (mod_order > 2) { - uint64_t sum_det = 0; - for (int k = 0; k < 4; k++) { - sum_det += (((uint32_t *)&determ_fin_128[0])[k]); - } - // Add bias to reduce rounding error - sum_det = (sum_det + 2) >> 2; - - int b = log2_approx(sum_det) - 8; - if (b > 0) { - mmtmpD2 = simde_mm_srai_epi32(determ_fin_128[0], b); - } else { - mmtmpD2 = simde_mm_slli_epi32(determ_fin_128[0], -b); - } - mmtmpD3 = simde_mm_unpacklo_epi32(mmtmpD2, mmtmpD2); - mmtmpD2 = simde_mm_unpackhi_epi32(mmtmpD2, mmtmpD2); - mmtmpD2 = simde_mm_packs_epi32(mmtmpD3, mmtmpD2); - - // Layer 0 - ul_ch_mag128_0[0] = mmtmpD2; - ul_ch_mag128b_0[0] = mmtmpD2; - ul_ch_mag128c_0[0] = mmtmpD2; - ul_ch_mag128_0[0] = simde_mm_mulhi_epi16(ul_ch_mag128_0[0], QAM_amp128); - ul_ch_mag128_0[0] = simde_mm_slli_epi16(ul_ch_mag128_0[0], 1); - ul_ch_mag128b_0[0] = simde_mm_mulhi_epi16(ul_ch_mag128b_0[0], QAM_amp128b); - ul_ch_mag128b_0[0] = simde_mm_slli_epi16(ul_ch_mag128b_0[0], 1); - ul_ch_mag128c_0[0] = simde_mm_mulhi_epi16(ul_ch_mag128c_0[0], QAM_amp128c); - ul_ch_mag128c_0[0] = simde_mm_slli_epi16(ul_ch_mag128c_0[0], 1); - - // Layer 1 - ul_ch_mag128_1[0] = mmtmpD2; - ul_ch_mag128b_1[0] = mmtmpD2; - ul_ch_mag128c_1[0] = mmtmpD2; - ul_ch_mag128_1[0] = simde_mm_mulhi_epi16(ul_ch_mag128_1[0], QAM_amp128); - ul_ch_mag128_1[0] = simde_mm_slli_epi16(ul_ch_mag128_1[0], 1); - ul_ch_mag128b_1[0] = simde_mm_mulhi_epi16(ul_ch_mag128b_1[0], QAM_amp128b); - ul_ch_mag128b_1[0] = simde_mm_slli_epi16(ul_ch_mag128b_1[0], 1); - ul_ch_mag128c_1[0] = simde_mm_mulhi_epi16(ul_ch_mag128c_1[0], QAM_amp128c); - ul_ch_mag128c_1[0] = simde_mm_slli_epi16(ul_ch_mag128c_1[0], 1); - } - - // multiply by channel Inv - //rxdataF_zf128_0 = rxdataF_comp128_0*d - b*rxdataF_comp128_1 - //rxdataF_zf128_1 = rxdataF_comp128_1*a - c*rxdataF_comp128_0 - //printf("layer_1 \n"); - mmtmpD0 = nr_ulsch_comp_muli_sum(rxdataF_comp128_0[0], - after_mf_d_128[0], - rxdataF_comp128_1[0], - after_mf_b_128[0], - determ_fin_128[0]); - - //printf("layer_2 \n"); - mmtmpD1 = nr_ulsch_comp_muli_sum(rxdataF_comp128_1[0], - after_mf_a_128[0], - rxdataF_comp128_0[0], - after_mf_c_128[0], - determ_fin_128[0]); - - rxdataF_comp128_0[0] = mmtmpD0; - rxdataF_comp128_1[0] = mmtmpD1; - -#ifdef DEBUG_DLSCH_DEMOD - printf("\n Rx signal after ZF l%d rb%d\n",symbol,rb); - print_shorts(" Rx layer 1:",(int16_t*)&rxdataF_comp128_0[0]); - print_shorts(" Rx layer 2:",(int16_t*)&rxdataF_comp128_1[0]); -#endif - determ_fin_128 += 1; - ul_ch_mag128_0 += 1; - ul_ch_mag128_1 += 1; - ul_ch_mag128b_0 += 1; - ul_ch_mag128b_1 += 1; - ul_ch_mag128c_0 += 1; - ul_ch_mag128c_1 += 1; - rxdataF_comp128_0 += 1; - rxdataF_comp128_1 += 1; - after_mf_a_128 += 1; - after_mf_b_128 += 1; - after_mf_c_128 += 1; - after_mf_d_128 += 1; - } - return(0); -} - static void inner_rx(PHY_VARS_gNB *gNB, int slot, NR_DL_FRAME_PARMS *frame_parms, @@ -884,43 +286,44 @@ static void inner_rx(PHY_VARS_gNB *gNB, start_meas(ulsch_llr); if (nb_layer == 2) { if (rel15_ul->qam_mod_order <= 6) { - nr_ulsch_compute_ML_llr((c16_t *)&pusch_vars->rxdataF_comp[0][symbol * buffer_length], - (c16_t *)&pusch_vars->rxdataF_comp[nb_rx_ant][symbol * buffer_length], - rxF_ch_maga[0], - rxF_ch_maga[1], - llr[0], - llr[1], - rho[0][1], - rho[1][0], - pusch_vars->ul_valid_re_per_slot[symbol], - rel15_ul->qam_mod_order); + nr_compute_ML_llr((c16_t *)&pusch_vars->rxdataF_comp[0][symbol * buffer_length], + (c16_t *)&pusch_vars->rxdataF_comp[nb_rx_ant][symbol * buffer_length], + rxF_ch_maga[0], + rxF_ch_maga[1], + llr[0], + llr[1], + rho[0][1], + rho[1][0], + pusch_vars->ul_valid_re_per_slot[symbol], + rel15_ul->qam_mod_order); } else { - nr_ulsch_mmse_2layers(pusch_vars->rxdataF_comp, - buffer_length, - nb_rx_ant, - rxF_ch_maga, - rxF_ch_magb, - rxF_ch_magc, - chFext, - rel15_ul->rb_size, - rel15_ul->qam_mod_order, - pusch_vars->log2_maxh, - symbol, - pusch_vars->ul_valid_re_per_slot[symbol], - nvar); + nr_mmse_2layers(pusch_vars->rxdataF_comp, + buffer_length, + nb_rx_ant, + nb_layer, + rxF_ch_maga, + rxF_ch_magb, + rxF_ch_magc, + chFext, + rel15_ul->rb_size, + rel15_ul->qam_mod_order, + pusch_vars->log2_maxh, + symbol, + pusch_vars->ul_valid_re_per_slot[symbol], + nvar); } } if (nb_layer != 2 || rel15_ul->qam_mod_order > 6) for (int aatx = 0; aatx < nb_layer; aatx++) - nr_ulsch_compute_llr(&pusch_vars->rxdataF_comp[aatx * nb_rx_ant][symbol * buffer_length], - rxF_ch_maga[aatx], - rxF_ch_magb[aatx], - rxF_ch_magc[aatx], - llr[aatx], - pusch_vars->ul_valid_re_per_slot[symbol], - symbol, - rel15_ul->qam_mod_order); + nr_compute_llr(&pusch_vars->rxdataF_comp[aatx * nb_rx_ant][symbol * buffer_length], + rxF_ch_maga[aatx], + rxF_ch_magb[aatx], + rxF_ch_magc[aatx], + llr[aatx], + pusch_vars->ul_valid_re_per_slot[symbol], + symbol, + rel15_ul->qam_mod_order); stop_meas(ulsch_llr); } diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c deleted file mode 100644 index 318da26b75..0000000000 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c +++ /dev/null @@ -1,1796 +0,0 @@ -/* - * SPDX-License-Identifier: LicenseRef-CSSL-1.0 - */ - -/*! - * \brief Top-level routines for LLR computation of the PDSCH physical channel - */ - -#include "PHY/defs_gNB.h" -#include "PHY/sse_intrin.h" -#include "nr_phy_common.h" - -#ifdef __aarch64__ -#define USE_128BIT -#endif - -void nr_ulsch_compute_llr(c16_t *rxdataF_comp, - c16_t *ul_ch_mag, - c16_t *ul_ch_magb, - c16_t *ul_ch_magc, - int16_t *ulsch_llr, - uint32_t nb_re, - uint8_t symbol, - uint8_t mod_order) -{ - switch(mod_order) { - case 2: - nr_qpsk_llr(rxdataF_comp, ulsch_llr, nb_re); - break; - case 4: - nr_16qam_llr(rxdataF_comp, ul_ch_mag, ulsch_llr, nb_re); - break; - case 6: - nr_64qam_llr(rxdataF_comp, ul_ch_mag, ul_ch_magb, ulsch_llr, nb_re); - break; - case 8: - nr_256qam_llr(rxdataF_comp, ul_ch_mag, ul_ch_magb, ul_ch_magc, ulsch_llr, nb_re); - break; - default: - AssertFatal(false, "nr_ulsch_compute_llr: invalid Qm value, symbol = %d, Qm = %d\n",symbol, mod_order); - break; - } -} - -/* - * This function computes the LLRs of stream 0 (s_0) in presence of the interfering stream 1 (s_1) assuming that both symbols are - * QPSK. It can be used for both MU-MIMO interference-aware receiver or for SU-MIMO receivers. - * - * Input: - * stream0_in: MF filter output for 1st stream, i.e., y0' = h0'*y0 - * stream1_in: MF filter output for 2nd stream, i.e., y1' = h1'*y0 - * rho01: Channel cross correlation, i.e., rho01 = h0'*h1 - * length: Number of resource elements - * - * Output: - * stream0_out: Output LLRs for 1st stream - */ -void nr_ulsch_qpsk_qpsk(c16_t *stream0_in, c16_t *stream1_in, int16_t *stream0_out, c16_t *rho01, uint32_t length) -{ -#ifdef USE_128BIT - simde__m128i *rho01_128i = (simde__m128i *)rho01; - simde__m128i *stream0_128i_in = (simde__m128i *)stream0_in; - simde__m128i *stream1_128i_in = (simde__m128i *)stream1_in; - simde__m128i *stream0_128i_out = (simde__m128i *)stream0_out; - simde__m128i ONE_OVER_2_SQRT_2 = simde_mm_set1_epi16(23170); // round(2 ^ 16 / (2 * sqrt(2))) - - // In each iteration, we take 8 complex symbols - for (int i = 0; i < length >> 2; i += 2) { - - /// Compute real and imaginary parts of MF output for stream 0 (desired stream) - simde__m128i y0r, y0i; - oai_mm_separate_real_imag_parts(&y0r, &y0i, stream0_128i_in[i], stream0_128i_in[i + 1]); - simde__m128i y0r_over2 = simde_mm_mulhi_epi16(y0r, ONE_OVER_2_SQRT_2); - y0r_over2 = simde_mm_slli_epi16(y0r_over2, 1); // y0r_over2 = Re(y0) / sqrt(2) - simde__m128i y0i_over2 = simde_mm_mulhi_epi16(y0i, ONE_OVER_2_SQRT_2); - y0i_over2 = simde_mm_slli_epi16(y0i_over2, 1); // y0i_over2 = Im(y0) / sqrt(2) - - /// Compute real and imaginary parts of MF output for stream 1 (interference stream) - simde__m128i y1r_over2, y1i_over2; - oai_mm_separate_real_imag_parts(&y1r_over2, &y1i_over2, stream1_128i_in[i], stream1_128i_in[i + 1]); - y1r_over2 = simde_mm_srai_epi16(y1r_over2, 1); // y1r_over2 = Re(y1) / 2 - y1i_over2 = simde_mm_srai_epi16(y1i_over2, 1); // y1i_over2 = Im(y1) / 2 - - /// Get real and imaginary parts of rho - simde__m128i rhor, rhoi; - oai_mm_separate_real_imag_parts(&rhor, &rhoi, rho01_128i[i], rho01_128i[i + 1]); - - /// Compute |psi_r| and |psi_i| - - // psi_r = rhor * xR + rhoi * xI - // psi_i = rhor * xI - rhoi * xR - - // Put (rho_r + rho_i)/(2*sqrt(2)) in rho_p - // rhor * xR + rhoi * xI --> xR = 1/sqrt(2) and xI = 1/sqrt(2) - // rhor * xI - rhoi * xR --> xR = -1/sqrt(2) and xI = 1/sqrt(2) - simde__m128i rho_p = simde_mm_adds_epi16(rhor, rhoi); // rho_p = Re(rho) + Im(rho) - rho_p = simde_mm_mulhi_epi16(rho_p, ONE_OVER_2_SQRT_2); // rho_p = rho_p / (2*sqrt(2)) - - // Put (rho_r - rho_i)/(2*sqrt(2)) in rho_m - // rhor * xR + rhoi * xI --> xR = 1/sqrt(2) and xI = -1/sqrt(2) - // rhor * xI - rhoi * xR --> xR = 1/sqrt(2) and xI = 1/sqrt(2) - simde__m128i rho_m = simde_mm_subs_epi16(rhor, rhoi); // rho_m = Re(rho) - Im(rho) - rho_m = simde_mm_mulhi_epi16(rho_m, ONE_OVER_2_SQRT_2); // rho_m = rho_m / (2*sqrt(2)) - - // xR = 1/sqrt(2) and xI = 1/sqrt(2) - simde__m128i abs_psi_rpm = simde_mm_subs_epi16(rho_p, y1r_over2); // psi_rpm = rho_p - y1r/2 - abs_psi_rpm = simde_mm_abs_epi16(abs_psi_rpm); // abs_psi_rpm = |psi_rpm| - - // xR = 1/sqrt(2) and xI = 1/sqrt(2) - simde__m128i abs_psi_imm = simde_mm_subs_epi16(rho_m, y1i_over2); // psi_imm = rho_m - y1i/2 - abs_psi_imm = simde_mm_abs_epi16(abs_psi_imm); // abs_psi_imm = |psi_imm| - - // xR = 1/sqrt(2) and xI = -1/sqrt(2) - simde__m128i abs_psi_rmm = simde_mm_subs_epi16(rho_m, y1r_over2); // psi_rmm = rho_m - y1r/2 - abs_psi_rmm = simde_mm_abs_epi16(abs_psi_rmm); // abs_psi_rmm = |psi_rmm| - - // xR = -1/sqrt(2) and xI = 1/sqrt(2) - simde__m128i abs_psi_ipm = simde_mm_subs_epi16(rho_p, y1i_over2); // psi_ipm = rho_p - y1i/2 - abs_psi_ipm = simde_mm_abs_epi16(abs_psi_ipm); // abs_psi_ipm = |psi_ipm| - - // xR = -1/sqrt(2) and xI = -1/sqrt(2) - simde__m128i abs_psi_rpp = simde_mm_adds_epi16(rho_p, y1r_over2); // psi_rpp = rho_p + y1r/2 - abs_psi_rpp = simde_mm_abs_epi16(abs_psi_rpp); // abs_psi_rpp = |psi_rpp| - - // xR = -1/sqrt(2) and xI = -1/sqrt(2) - simde__m128i abs_psi_imp = simde_mm_adds_epi16(rho_m, y1i_over2); // psi_imp = rho_m + y1i/2 - abs_psi_imp = simde_mm_abs_epi16(abs_psi_imp); // abs_psi_imp = |psi_imp| - - // xR = -1/sqrt(2) and xI = 1/sqrt(2) - simde__m128i abs_psi_rmp = simde_mm_adds_epi16(rho_m, y1r_over2); // psi_rmp = rho_m + y1r/2 - abs_psi_rmp = simde_mm_abs_epi16(abs_psi_rmp); // abs_psi_rmp = |psi_rmp| - - // xR = 1/sqrt(2) and xI = -1/sqrt(2) - simde__m128i abs_psi_ipp = simde_mm_adds_epi16(rho_p, y1i_over2); // psi_ipm = rho_p + y1i/2 - abs_psi_ipp = simde_mm_abs_epi16(abs_psi_ipp); // abs_psi_ipp = |psi_ipm| - - /// Compute bit metrics (lambda) - - // lambda = max { |psi_r - y1r| * |x2R| + |psi_i - y1i| * |x2I| + y0r * xR + y0i * xI} - - // xR = 1/sqrt(2) and xI = 1/sqrt(2) - // For numerator: bit_met_num_re_p = abs_psi_rpm + abs_psi_imm + y0r/sqrt(2) + y0i/sqrt(2) - simde__m128i bit_met_num_re_p = simde_mm_adds_epi16(abs_psi_rpm, abs_psi_imm); - bit_met_num_re_p = simde_mm_adds_epi16(bit_met_num_re_p, y0r_over2); - bit_met_num_re_p = simde_mm_adds_epi16(bit_met_num_re_p, y0i_over2); - - // xR = 1/sqrt(2) and xI = -1/sqrt(2) - // For numerator: bit_met_num_re_m = abs_psi_rmm + abs_psi_ipp + y0r/sqrt(2) - y0i/sqrt(2) - simde__m128i bit_met_num_re_m = simde_mm_adds_epi16(abs_psi_rmm, abs_psi_ipp); - bit_met_num_re_m = simde_mm_adds_epi16(bit_met_num_re_m, y0r_over2); - bit_met_num_re_m = simde_mm_subs_epi16(bit_met_num_re_m, y0i_over2); - - // xR = -1/sqrt(2) and xI = 1/sqrt(2) - // For denominator: bit_met_den_re_p = abs_psi_rmp + abs_psi_ipm - y0r/sqrt(2) + y0i/sqrt(2) - simde__m128i bit_met_den_re_p = simde_mm_adds_epi16(abs_psi_rmp, abs_psi_ipm); - bit_met_den_re_p = simde_mm_subs_epi16(bit_met_den_re_p, y0r_over2); - bit_met_den_re_p = simde_mm_adds_epi16(bit_met_den_re_p, y0i_over2); - - // xR = -1/sqrt(2) and xI = -1/sqrt(2) - // For denominator: bit_met_den_re_m = abs_psi_rpp + abs_psi_imp - y0r/sqrt(2) - y0i/sqrt(2) - simde__m128i bit_met_den_re_m = simde_mm_adds_epi16(abs_psi_rpp, abs_psi_imp); - bit_met_den_re_m = simde_mm_subs_epi16(bit_met_den_re_m, y0r_over2); - bit_met_den_re_m = simde_mm_subs_epi16(bit_met_den_re_m, y0i_over2); - - // xR = 1/sqrt(2) and xI = 1/sqrt(2) - // For numerator: bit_met_num_im_p = abs_psi_rpm + abs_psi_imm + y0r/sqrt(2) + y0i/sqrt(2) - simde__m128i bit_met_num_im_p = simde_mm_adds_epi16(abs_psi_rpm, abs_psi_imm); - bit_met_num_im_p = simde_mm_adds_epi16(bit_met_num_im_p, y0r_over2); - bit_met_num_im_p = simde_mm_adds_epi16(bit_met_num_im_p, y0i_over2); - - // xR = -1/sqrt(2) and xI = 1/sqrt(2) - // For numerator: bit_met_num_im_m = abs_psi_rmp + abs_psi_ipm - y0r/sqrt(2) + y0i/sqrt(2) - simde__m128i bit_met_num_im_m = simde_mm_adds_epi16(abs_psi_rmp, abs_psi_ipm); - bit_met_num_im_m = simde_mm_subs_epi16(bit_met_num_im_m, y0r_over2); - bit_met_num_im_m = simde_mm_adds_epi16(bit_met_num_im_m, y0i_over2); - - // xR = 1/sqrt(2) and xI = -1/sqrt(2) - // For denominator: bit_met_den_im_p = abs_psi_rmm + abs_psi_ipp + y0r/sqrt(2) - y0i/sqrt(2) - simde__m128i bit_met_den_im_p = simde_mm_adds_epi16(abs_psi_rmm, abs_psi_ipp); - bit_met_den_im_p = simde_mm_adds_epi16(bit_met_den_im_p, y0r_over2); - bit_met_den_im_p = simde_mm_subs_epi16(bit_met_den_im_p, y0i_over2); - - // xR = -1/sqrt(2) and xI = -1/sqrt(2) - // For denominator: bit_met_den_im_m = abs_psi_rpp + abs_psi_imp - y0r/sqrt(2)- y0i/sqrt(2) - simde__m128i bit_met_den_im_m = simde_mm_adds_epi16(abs_psi_rpp, abs_psi_imp); - bit_met_den_im_m = simde_mm_subs_epi16(bit_met_den_im_m, y0r_over2); - bit_met_den_im_m = simde_mm_subs_epi16(bit_met_den_im_m, y0i_over2); - - /// Compute the LLRs - - // LLR = lambda(c==1) - lambda(c==0) - - simde__m128i logmax_num_re0 = simde_mm_max_epi16(bit_met_num_re_p, bit_met_num_re_m); // LLR of the first bit: Bit = 1 - simde__m128i logmax_den_re0 = simde_mm_max_epi16(bit_met_den_re_p, bit_met_den_re_m); // LLR of the first bit: Bit = 0 - simde__m128i logmax_num_im0 = simde_mm_max_epi16(bit_met_num_im_p, bit_met_num_im_m); // LLR of the second bit: Bit = 1 - simde__m128i logmax_den_im0 = simde_mm_max_epi16(bit_met_den_im_p, bit_met_den_im_m); // LLR of the second bit: Bit = 0 - - y0r = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); // LLR of first bit [L1(1), L1(2), L1(3), L1(4)] - y0i = simde_mm_subs_epi16(logmax_num_im0, logmax_den_im0); // LLR of second bit [L2(1), L2(2), L2(3), L2(4)] - - // [L1(1), L2(1), L1(2), L2(2)] - simde_mm_storeu_si128(&stream0_128i_out[i], simde_mm_unpacklo_epi16(y0r, y0i)); - - // false if only 2 REs remain - if (i < ((length >> 1) - 1)) { - simde_mm_storeu_si128(&stream0_128i_out[i + 1], simde_mm_unpackhi_epi16(y0r, y0i)); - } - } -#else - - simde__m256i *rho01_256i = (simde__m256i *)rho01; - simde__m256i *stream0_256i_in = (simde__m256i *)stream0_in; - simde__m256i *stream1_256i_in = (simde__m256i *)stream1_in; - simde__m256i *stream0_256i_out = (simde__m256i *)stream0_out; - simde__m256i ONE_OVER_2_SQRT_2 = simde_mm256_set1_epi16(23170); // round(2 ^ 16 / (2 * sqrt(2))) - - // In each iteration, we take 16 complex symbols - for (int i = 0; i < length >> 3; i += 2) { - - /// Compute real and imaginary parts of MF output for stream 0 (desired stream) - simde__m256i y0r, y0i; - oai_mm256_separate_real_imag_parts(&y0r, &y0i, stream0_256i_in[i], stream0_256i_in[i + 1]); - simde__m256i y0r_over2 = simde_mm256_mulhi_epi16(y0r, ONE_OVER_2_SQRT_2); - y0r_over2 = simde_mm256_slli_epi16(y0r_over2, 1); // y0r_over2 = Re(y0) / sqrt(2) - simde__m256i y0i_over2 = simde_mm256_mulhi_epi16(y0i, ONE_OVER_2_SQRT_2); - y0i_over2 = simde_mm256_slli_epi16(y0i_over2, 1); // y0i_over2 = Im(y0) / sqrt(2) - - /// Compute real and imaginary parts of MF output for stream 1 (interference stream) - simde__m256i y1r_over2, y1i_over2; - oai_mm256_separate_real_imag_parts(&y1r_over2, &y1i_over2, stream1_256i_in[i], stream1_256i_in[i + 1]); - y1r_over2 = simde_mm256_srai_epi16(y1r_over2, 1); // y1r_over2 = Re(y1) / 2 - y1i_over2 = simde_mm256_srai_epi16(y1i_over2, 1); // y1i_over2 = Im(y1) / 2 - - /// Get real and imaginary parts of rho - simde__m256i rhor, rhoi; - oai_mm256_separate_real_imag_parts(&rhor, &rhoi, rho01_256i[i], rho01_256i[i + 1]); - - /// Compute |psi_r| and |psi_i| - - // psi_r = rhor * xR + rhoi * xI - // psi_i = rhor * xI - rhoi * xR - - // Put (rho_r + rho_i)/(2*sqrt(2)) in rho_p - // rhor * xR + rhoi * xI --> xR = 1/sqrt(2) and xI = 1/sqrt(2) - // rhor * xI - rhoi * xR --> xR = -1/sqrt(2) and xI = 1/sqrt(2) - simde__m256i rho_p = simde_mm256_adds_epi16(rhor, rhoi); // rho_p = Re(rho) + Im(rho) - rho_p = simde_mm256_mulhi_epi16(rho_p, ONE_OVER_2_SQRT_2); // rho_p = rho_p / (2*sqrt(2)) - - // Put (rho_r - rho_i)/(2*sqrt(2)) in rho_m - // rhor * xR + rhoi * xI --> xR = 1/sqrt(2) and xI = -1/sqrt(2) - // rhor * xI - rhoi * xR --> xR = 1/sqrt(2) and xI = 1/sqrt(2) - simde__m256i rho_m = simde_mm256_subs_epi16(rhor, rhoi); // rho_m = Re(rho) - Im(rho) - rho_m = simde_mm256_mulhi_epi16(rho_m, ONE_OVER_2_SQRT_2); // rho_m = rho_m / (2*sqrt(2)) - - // xR = 1/sqrt(2) and xI = 1/sqrt(2) - simde__m256i abs_psi_rpm = simde_mm256_subs_epi16(rho_p, y1r_over2); // psi_rpm = rho_p - y1r/2 - abs_psi_rpm = simde_mm256_abs_epi16(abs_psi_rpm); // abs_psi_rpm = |psi_rpm| - - // xR = 1/sqrt(2) and xI = 1/sqrt(2) - simde__m256i abs_psi_imm = simde_mm256_subs_epi16(rho_m, y1i_over2); // psi_imm = rho_m - y1i/2 - abs_psi_imm = simde_mm256_abs_epi16(abs_psi_imm); // abs_psi_imm = |psi_imm| - - // xR = 1/sqrt(2) and xI = -1/sqrt(2) - simde__m256i abs_psi_rmm = simde_mm256_subs_epi16(rho_m, y1r_over2); // psi_rmm = rho_m - y1r/2 - abs_psi_rmm = simde_mm256_abs_epi16(abs_psi_rmm); // abs_psi_rmm = |psi_rmm| - - // xR = -1/sqrt(2) and xI = 1/sqrt(2) - simde__m256i abs_psi_ipm = simde_mm256_subs_epi16(rho_p, y1i_over2); // psi_ipm = rho_p - y1i/2 - abs_psi_ipm = simde_mm256_abs_epi16(abs_psi_ipm); // abs_psi_ipm = |psi_ipm| - - // xR = -1/sqrt(2) and xI = -1/sqrt(2) - simde__m256i abs_psi_rpp = simde_mm256_adds_epi16(rho_p, y1r_over2); // psi_rpp = rho_p + y1r/2 - abs_psi_rpp = simde_mm256_abs_epi16(abs_psi_rpp); // abs_psi_rpp = |psi_rpp| - - // xR = -1/sqrt(2) and xI = -1/sqrt(2) - simde__m256i abs_psi_imp = simde_mm256_adds_epi16(rho_m, y1i_over2); // psi_imp = rho_m + y1i/2 - abs_psi_imp = simde_mm256_abs_epi16(abs_psi_imp); // abs_psi_imp = |psi_imp| - - // xR = -1/sqrt(2) and xI = 1/sqrt(2) - simde__m256i abs_psi_rmp = simde_mm256_adds_epi16(rho_m, y1r_over2); // psi_rmp = rho_m + y1r/2 - abs_psi_rmp = simde_mm256_abs_epi16(abs_psi_rmp); // abs_psi_rmp = |psi_rmp| - - // xR = 1/sqrt(2) and xI = -1/sqrt(2) - simde__m256i abs_psi_ipp = simde_mm256_adds_epi16(rho_p, y1i_over2); // psi_ipm = rho_p + y1i/2 - abs_psi_ipp = simde_mm256_abs_epi16(abs_psi_ipp); // abs_psi_ipp = |psi_ipm| - - /// Compute bit metrics (lambda) - - // lambda = max { |psi_r - y1r| * |x2R| + |psi_i - y1i| * |x2I| + y0r * xR + y0i * xI} - - // xR = 1/sqrt(2) and xI = 1/sqrt(2) - // For numerator: bit_met_num_re_p = abs_psi_rpm + abs_psi_imm + y0r/sqrt(2) + y0i/sqrt(2) - simde__m256i bit_met_num_re_p = simde_mm256_adds_epi16(abs_psi_rpm, abs_psi_imm); - bit_met_num_re_p = simde_mm256_adds_epi16(bit_met_num_re_p, y0r_over2); - bit_met_num_re_p = simde_mm256_adds_epi16(bit_met_num_re_p, y0i_over2); - - // xR = 1/sqrt(2) and xI = -1/sqrt(2) - // For numerator: bit_met_num_re_m = abs_psi_rmm + abs_psi_ipp + y0r/sqrt(2) - y0i/sqrt(2) - simde__m256i bit_met_num_re_m = simde_mm256_adds_epi16(abs_psi_rmm, abs_psi_ipp); - bit_met_num_re_m = simde_mm256_adds_epi16(bit_met_num_re_m, y0r_over2); - bit_met_num_re_m = simde_mm256_subs_epi16(bit_met_num_re_m, y0i_over2); - - // xR = -1/sqrt(2) and xI = 1/sqrt(2) - // For denominator: bit_met_den_re_p = abs_psi_rmp + abs_psi_ipm - y0r/sqrt(2) + y0i/sqrt(2) - simde__m256i bit_met_den_re_p = simde_mm256_adds_epi16(abs_psi_rmp, abs_psi_ipm); - bit_met_den_re_p = simde_mm256_subs_epi16(bit_met_den_re_p, y0r_over2); - bit_met_den_re_p = simde_mm256_adds_epi16(bit_met_den_re_p, y0i_over2); - - // xR = -1/sqrt(2) and xI = -1/sqrt(2) - // For denominator: bit_met_den_re_m = abs_psi_rpp + abs_psi_imp - y0r/sqrt(2) - y0i/sqrt(2) - simde__m256i bit_met_den_re_m = simde_mm256_adds_epi16(abs_psi_rpp, abs_psi_imp); - bit_met_den_re_m = simde_mm256_subs_epi16(bit_met_den_re_m, y0r_over2); - bit_met_den_re_m = simde_mm256_subs_epi16(bit_met_den_re_m, y0i_over2); - - // xR = 1/sqrt(2) and xI = 1/sqrt(2) - // For numerator: bit_met_num_im_p = abs_psi_rpm + abs_psi_imm + y0r/sqrt(2) + y0i/sqrt(2) - simde__m256i bit_met_num_im_p = simde_mm256_adds_epi16(abs_psi_rpm, abs_psi_imm); - bit_met_num_im_p = simde_mm256_adds_epi16(bit_met_num_im_p, y0r_over2); - bit_met_num_im_p = simde_mm256_adds_epi16(bit_met_num_im_p, y0i_over2); - - // xR = -1/sqrt(2) and xI = 1/sqrt(2) - // For numerator: bit_met_num_im_m = abs_psi_rmp + abs_psi_ipm - y0r/sqrt(2) + y0i/sqrt(2) - simde__m256i bit_met_num_im_m = simde_mm256_adds_epi16(abs_psi_rmp, abs_psi_ipm); - bit_met_num_im_m = simde_mm256_subs_epi16(bit_met_num_im_m, y0r_over2); - bit_met_num_im_m = simde_mm256_adds_epi16(bit_met_num_im_m, y0i_over2); - - // xR = 1/sqrt(2) and xI = -1/sqrt(2) - // For denominator: bit_met_den_im_p = abs_psi_rmm + abs_psi_ipp + y0r/sqrt(2) - y0i/sqrt(2) - simde__m256i bit_met_den_im_p = simde_mm256_adds_epi16(abs_psi_rmm, abs_psi_ipp); - bit_met_den_im_p = simde_mm256_adds_epi16(bit_met_den_im_p, y0r_over2); - bit_met_den_im_p = simde_mm256_subs_epi16(bit_met_den_im_p, y0i_over2); - - // xR = -1/sqrt(2) and xI = -1/sqrt(2) - // For denominator: bit_met_den_im_m = abs_psi_rpp + abs_psi_imp - y0r/sqrt(2)- y0i/sqrt(2) - simde__m256i bit_met_den_im_m = simde_mm256_adds_epi16(abs_psi_rpp, abs_psi_imp); - bit_met_den_im_m = simde_mm256_subs_epi16(bit_met_den_im_m, y0r_over2); - bit_met_den_im_m = simde_mm256_subs_epi16(bit_met_den_im_m, y0i_over2); - - /// Compute the LLRs - - // LLR = lambda(c==1) - lambda(c==0) - - simde__m256i logmax_num_re0 = simde_mm256_max_epi16(bit_met_num_re_p, bit_met_num_re_m); // LLR of the first bit: Bit = 1 - simde__m256i logmax_den_re0 = simde_mm256_max_epi16(bit_met_den_re_p, bit_met_den_re_m); // LLR of the first bit: Bit = 0 - simde__m256i logmax_num_im0 = simde_mm256_max_epi16(bit_met_num_im_p, bit_met_num_im_m); // LLR of the second bit: Bit = 1 - simde__m256i logmax_den_im0 = simde_mm256_max_epi16(bit_met_den_im_p, bit_met_den_im_m); // LLR of the second bit: Bit = 0 - - y0r = simde_mm256_subs_epi16(logmax_num_re0, logmax_den_re0); // LLR of first bit [L1(1), L1(2), L1(3), L1(4), L1(5), L1(6), L1(7), L1(8)] - y0i = simde_mm256_subs_epi16(logmax_num_im0, logmax_den_im0); // LLR of second bit [L2(1), L2(2), L2(3), L2(4), L2(5), L2(6), L2(7), L2(8)] - - // [L1(1), L2(1), L1(2), L2(2) ...] - simde__m128i * stream0_128i_out = (simde__m128i *)&stream0_256i_out[i]; - simde__m128i * y0r_128 = (simde__m128i *) &y0r; - simde__m128i * y0i_128 = (simde__m128i *) &y0i; - simde_mm_storeu_si128(&stream0_128i_out[0], simde_mm_unpacklo_epi16(y0r_128[0], y0i_128[0])); - simde_mm_storeu_si128(&stream0_128i_out[1], simde_mm_unpackhi_epi16(y0r_128[0], y0i_128[0])); - - // false if only 4 REs remain - if (i < ((length >> 2) - 1)) - { - simde__m128i * stream0_128i_out = (simde__m128i *)&stream0_256i_out[i + 1]; - simde_mm_storeu_si128(&stream0_128i_out[0], simde_mm_unpacklo_epi16(y0r_128[1], y0i_128[1])); - simde_mm_storeu_si128(&stream0_128i_out[1], simde_mm_unpackhi_epi16(y0r_128[1], y0i_128[1])); - } - } -#endif -} - -#ifdef USE_128BIT -// calculate interference magnitude -// tmp_result = ones in shorts corr. to interval 2<=x<=4, tmp_result2 interval < 2, tmp_result3 interval 46 -static inline simde__m128i interference_abs_64qam_epi16(simde__m128i psi, - simde__m128i int_ch_mag, - simde__m128i int_two_ch_mag, - simde__m128i int_three_ch_mag, - simde__m128i c1, - simde__m128i c3, - simde__m128i c5, - simde__m128i c7) -{ - simde__m128i tmp_result = simde_mm_cmpgt_epi16(int_two_ch_mag, psi); - simde__m128i tmp_result3 = simde_mm_xor_si128(tmp_result, allones128()); - simde__m128i tmp_result2 = simde_mm_cmpgt_epi16(int_ch_mag, psi); - tmp_result = simde_mm_xor_si128(tmp_result, tmp_result2); - simde__m128i tmp_result4 = simde_mm_cmpgt_epi16(psi, int_three_ch_mag); - tmp_result3 = simde_mm_xor_si128(tmp_result3, tmp_result4); - tmp_result = simde_mm_and_si128(tmp_result, c3); - tmp_result2 = simde_mm_and_si128(tmp_result2, c1); - tmp_result3 = simde_mm_and_si128(tmp_result3, c5); - tmp_result4 = simde_mm_and_si128(tmp_result4, c7); - tmp_result = simde_mm_or_si128(tmp_result, tmp_result2); - tmp_result3 = simde_mm_or_si128(tmp_result3, tmp_result4); - return simde_mm_or_si128(tmp_result, tmp_result3); -} - -// Calculates psi_a = psi_r * a_r + psi_i * a_i -static inline simde__m128i prodsum_psi_a_epi16(simde__m128i psi_r, simde__m128i a_r, simde__m128i psi_i, simde__m128i a_i) -{ - simde__m128i tmp_result = simde_mm_mulhi_epi16(psi_r, a_r); - tmp_result = simde_mm_slli_epi16(tmp_result, 1); - simde__m128i tmp_result2 = simde_mm_mulhi_epi16(psi_i, a_i); - tmp_result2 = simde_mm_slli_epi16(tmp_result2, 1); - return simde_mm_adds_epi16(tmp_result, tmp_result2); -} - -// Calculate interference magnitude -static inline simde__m128i interference_abs_epi16(simde__m128i psi, simde__m128i int_ch_mag, simde__m128i c1, simde__m128i c2) -{ - simde__m128i tmp_result = simde_mm_cmplt_epi16(psi, int_ch_mag); - simde__m128i tmp_result2 = simde_mm_xor_si128(tmp_result,allones128()); - tmp_result = simde_mm_and_si128(tmp_result, c1); - tmp_result2 = simde_mm_and_si128(tmp_result2, c2); - return simde_mm_or_si128(tmp_result, tmp_result2); -} - -// Calculates a_sq = int_ch_mag * (a_r^2 + a_i^2) * scale_factor -static inline simde__m128i square_a_epi16(simde__m128i a_r, simde__m128i a_i, simde__m128i int_ch_mag, simde__m128i scale_factor) -{ - simde__m128i tmp_result = simde_mm_mulhi_epi16(a_r, a_r); - tmp_result = simde_mm_slli_epi16(tmp_result, 1); - tmp_result = simde_mm_mulhi_epi16(tmp_result, scale_factor); - tmp_result = simde_mm_slli_epi16(tmp_result, 1); - tmp_result = simde_mm_mulhi_epi16(tmp_result, int_ch_mag); - tmp_result = simde_mm_slli_epi16(tmp_result, 1); - simde__m128i tmp_result2 = simde_mm_mulhi_epi16(a_i, a_i); - tmp_result2 = simde_mm_slli_epi16(tmp_result2, 1); - tmp_result2 = simde_mm_mulhi_epi16(tmp_result2, scale_factor); - tmp_result2 = simde_mm_slli_epi16(tmp_result2, 1); - tmp_result2 = simde_mm_mulhi_epi16(tmp_result2, int_ch_mag); - tmp_result2 = simde_mm_slli_epi16(tmp_result2, 1); - return simde_mm_adds_epi16(tmp_result, tmp_result2); -} - -// calculates a_sq = int_ch_mag*(a_r^2 + a_i^2)*scale_factor for 64-QAM -static inline simde__m128i square_a_64qam_epi16(simde__m128i a_r, simde__m128i a_i, simde__m128i int_ch_mag, simde__m128i scale_factor) -{ - simde__m128i tmp_result = simde_mm_mulhi_epi16(a_r, a_r); - tmp_result = simde_mm_slli_epi16(tmp_result, 1); - tmp_result = simde_mm_mulhi_epi16(tmp_result, scale_factor); - tmp_result = simde_mm_slli_epi16(tmp_result, 3); - tmp_result = simde_mm_mulhi_epi16(tmp_result, int_ch_mag); - tmp_result = simde_mm_slli_epi16(tmp_result, 1); - simde__m128i tmp_result2 = simde_mm_mulhi_epi16(a_i, a_i); - tmp_result2 = simde_mm_slli_epi16(tmp_result2, 1); - tmp_result2 = simde_mm_mulhi_epi16(tmp_result2, scale_factor); - tmp_result2 = simde_mm_slli_epi16(tmp_result2, 3); - tmp_result2 = simde_mm_mulhi_epi16(tmp_result2, int_ch_mag); - tmp_result2 = simde_mm_slli_epi16(tmp_result2, 1); - return simde_mm_adds_epi16(tmp_result, tmp_result2); -} - -static inline simde__m128i max_epi16(simde__m128i m0, simde__m128i m1, simde__m128i m2, simde__m128i m3, simde__m128i m4, simde__m128i m5, simde__m128i m6, simde__m128i m7) -{ - simde__m128i a0 = simde_mm_max_epi16(m0, m1); - simde__m128i a1 = simde_mm_max_epi16(m2, m3); - simde__m128i a2 = simde_mm_max_epi16(m4, m5); - simde__m128i a3 = simde_mm_max_epi16(m6, m7); - simde__m128i b0 = simde_mm_max_epi16(a0, a1); - simde__m128i b1 = simde_mm_max_epi16(a2, a3); - return simde_mm_max_epi16(b0, b1); -} - -#else - -// calculate interference magnitude -// tmp_result = ones in shorts corr. to interval 2<=x<=4, tmp_result2 interval < 2, tmp_result3 interval 46 -static inline simde__m256i interference_abs_64qam_epi16_256(simde__m256i psi, - simde__m256i int_ch_mag, - simde__m256i int_two_ch_mag, - simde__m256i int_three_ch_mag, - simde__m256i c1, - simde__m256i c3, - simde__m256i c5, - simde__m256i c7) -{ - simde__m256i tmp_result = simde_mm256_cmpgt_epi16(int_two_ch_mag, psi); - simde__m256i tmp_result3 = simde_mm256_xor_si256(tmp_result, allones256()); - simde__m256i tmp_result2 = simde_mm256_cmpgt_epi16(int_ch_mag, psi); - tmp_result = simde_mm256_xor_si256(tmp_result, tmp_result2); - simde__m256i tmp_result4 = simde_mm256_cmpgt_epi16(psi, int_three_ch_mag); - tmp_result3 = simde_mm256_xor_si256(tmp_result3, tmp_result4); - tmp_result = simde_mm256_and_si256(tmp_result, c3); - tmp_result2 = simde_mm256_and_si256(tmp_result2, c1); - tmp_result3 = simde_mm256_and_si256(tmp_result3, c5); - tmp_result4 = simde_mm256_and_si256(tmp_result4, c7); - tmp_result = simde_mm256_or_si256(tmp_result, tmp_result2); - tmp_result3 = simde_mm256_or_si256(tmp_result3, tmp_result4); - return simde_mm256_or_si256(tmp_result, tmp_result3); -} - -// calculates psi_a = psi_r*a_r + psi_i*a_i -static inline simde__m256i prodsum_psi_a_epi16_256(simde__m256i psi_r, simde__m256i a_r, simde__m256i psi_i, simde__m256i a_i) -{ - simde__m256i tmp_result = simde_mm256_mulhi_epi16(psi_r, a_r); - tmp_result = simde_mm256_slli_epi16(tmp_result, 1); - simde__m256i tmp_result2 = simde_mm256_mulhi_epi16(psi_i, a_i); - tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 1); - return simde_mm256_adds_epi16(tmp_result, tmp_result2); -} - -// Calculate interference magnitude -static inline simde__m256i interference_abs_epi16_256(simde__m256i psi, simde__m256i int_ch_mag, simde__m256i c1, simde__m256i c2) -{ - simde__m256i tmp_result = simde_mm256_cmpgt_epi16(int_ch_mag, psi); - simde__m256i tmp_result2 = simde_mm256_xor_si256(tmp_result, allones256()); - tmp_result = simde_mm256_and_si256(tmp_result, c1); - tmp_result2 = simde_mm256_and_si256(tmp_result2, c2); - return simde_mm256_or_si256(tmp_result, tmp_result2); -} - -// Calculates a_sq = int_ch_mag * (a_r^2 + a_i^2) * scale_factor -static inline simde__m256i square_a_epi16_256(simde__m256i a_r, simde__m256i a_i, simde__m256i int_ch_mag, simde__m256i scale_factor) -{ - simde__m256i tmp_result = simde_mm256_mulhi_epi16(a_r, a_r); - tmp_result = simde_mm256_slli_epi16(tmp_result, 1); - tmp_result = simde_mm256_mulhi_epi16(tmp_result, scale_factor); - tmp_result = simde_mm256_slli_epi16(tmp_result, 1); - tmp_result = simde_mm256_mulhi_epi16(tmp_result, int_ch_mag); - tmp_result = simde_mm256_slli_epi16(tmp_result, 1); - simde__m256i tmp_result2 = simde_mm256_mulhi_epi16(a_i, a_i); - tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 1); - tmp_result2 = simde_mm256_mulhi_epi16(tmp_result2, scale_factor); - tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 1); - tmp_result2 = simde_mm256_mulhi_epi16(tmp_result2, int_ch_mag); - tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 1); - return simde_mm256_adds_epi16(tmp_result, tmp_result2); -} - -// calculates a_sq = int_ch_mag*(a_r^2 + a_i^2)*scale_factor for 64-QAM -static inline simde__m256i square_a_64qam_epi16_256(simde__m256i a_r, simde__m256i a_i, simde__m256i int_ch_mag, simde__m256i scale_factor) -{ - simde__m256i tmp_result = simde_mm256_mulhi_epi16(a_r, a_r); - tmp_result = simde_mm256_slli_epi16(tmp_result, 1); - tmp_result = simde_mm256_mulhi_epi16(tmp_result, scale_factor); - tmp_result = simde_mm256_slli_epi16(tmp_result, 3); - tmp_result = simde_mm256_mulhi_epi16(tmp_result, int_ch_mag); - tmp_result = simde_mm256_slli_epi16(tmp_result, 1); - simde__m256i tmp_result2 = simde_mm256_mulhi_epi16(a_i, a_i); - tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 1); - tmp_result2 = simde_mm256_mulhi_epi16(tmp_result2, scale_factor); - tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 3); - tmp_result2 = simde_mm256_mulhi_epi16(tmp_result2, int_ch_mag); - tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 1); - return simde_mm256_adds_epi16(tmp_result, tmp_result2); -} - -static inline simde__m256i max_epi16_256(simde__m256i m0, simde__m256i m1, simde__m256i m2, simde__m256i m3, simde__m256i m4, simde__m256i m5, simde__m256i m6, simde__m256i m7) -{ - simde__m256i a0 = simde_mm256_max_epi16(m0, m1); - simde__m256i a1 = simde_mm256_max_epi16(m2, m3); - simde__m256i a2 = simde_mm256_max_epi16(m4, m5); - simde__m256i a3 = simde_mm256_max_epi16(m6, m7); - simde__m256i b0 = simde_mm256_max_epi16(a0, a1); - simde__m256i b1 = simde_mm256_max_epi16(a2, a3); - return simde_mm256_max_epi16(b0, b1); -} - -#endif - -/* - * This function computes the LLRs of stream 0 (s_0) in presence of the interfering stream 1 (s_1) assuming that both symbols are - * 16QAM. It can be used for both MU-MIMO interference-aware receiver or for SU-MIMO receivers. - * - * Input: - * stream0_in: MF filter output for 1st stream, i.e., y0' = h0'*y0 - * stream1_in: MF filter output for 2nd stream, i.e., y1' = h1'*y0 - * ch_mag: 2*h0/sqrt(10), [Re0 Im0 Re1 Im1] s.t. Im0=Re0, Im1=Re1, etc - * ch_mag_i: 2*h1/sqrt(10), [Re0 Im0 Re1 Im1] s.t. Im0=Re0, Im1=Re1, etc - * rho01: Channel cross correlation, i.e., rho01 = h0'*h1 - * length: Number of resource elements - * - * Output: - * stream0_out: Output LLRs for 1st stream - */ -void nr_ulsch_qam16_qam16(c16_t *stream0_in, - c16_t *stream1_in, - c16_t *ch_mag, - c16_t *ch_mag_i, - int16_t *stream0_out, - c16_t *rho01, - uint32_t length) -{ -#ifdef USE_128BIT - simde__m128i *rho01_128i = (simde__m128i *)rho01; - simde__m128i *stream0_128i_in = (simde__m128i *)stream0_in; - simde__m128i *stream1_128i_in = (simde__m128i *)stream1_in; - simde__m128i *stream0_128i_out = (simde__m128i *)stream0_out; - simde__m128i *ch_mag_128i = (simde__m128i *)ch_mag; - simde__m128i *ch_mag_128i_i = (simde__m128i *)ch_mag_i; - - simde__m128i ONE_OVER_SQRT_10 = simde_mm_set1_epi16(20724); // round(1/sqrt(10)*2^16) - simde__m128i ONE_OVER_SQRT_10_Q15 = simde_mm_set1_epi16(10362); // round(1/sqrt(10)*2^15) - simde__m128i THREE_OVER_SQRT_10 = simde_mm_set1_epi16(31086); // round(3/sqrt(10)*2^15) - simde__m128i SQRT_10_OVER_FOUR = simde_mm_set1_epi16(25905); // round(sqrt(10)/4*2^15) - simde__m128i ONE_OVER_TWO_SQRT_10 = simde_mm_set1_epi16(10362); // round(1/2/sqrt(10)*2^16) - simde__m128i NINE_OVER_TWO_SQRT_10 = simde_mm_set1_epi16(23315); // round(9/2/sqrt(10)*2^14) - simde__m128i ch_mag_des, ch_mag_int; - simde__m128i y0r_over_sqrt10; - simde__m128i y0i_over_sqrt10; - simde__m128i y0r_three_over_sqrt10; - simde__m128i y0i_three_over_sqrt10; - simde__m128i ch_mag_over_10; - simde__m128i ch_mag_over_2; - simde__m128i ch_mag_9_over_10; - - simde__m128i xmm0; - simde__m128i xmm1; - simde__m128i xmm2; - simde__m128i xmm3; - simde__m128i xmm4; - simde__m128i xmm5; - simde__m128i xmm6; - simde__m128i xmm7; - - simde__m128i rho_rpi; - simde__m128i rho_rmi; - simde__m128i rho_rs[8]; - simde__m128i psi_rs[16]; - simde__m128i psi_is[16]; - simde__m128i a_rs[16]; - simde__m128i a_is[16]; - simde__m128i psi_as[16]; - simde__m128i a_sqs[16]; - simde__m128i y0_s[8]; - - simde__m128i y0r; - simde__m128i y0i; - simde__m128i y1r; - simde__m128i y1i; - - // In one iteration, we deal with 8 REs - for (int i = 0; i < length >> 2; i += 2) { - - // Get rho - oai_mm_separate_real_imag_parts(&xmm2, &xmm3, rho01_128i[i], rho01_128i[i + 1]); - rho_rpi = simde_mm_adds_epi16(xmm2, xmm3); // rho = Re(rho) + Im(rho) - rho_rmi = simde_mm_subs_epi16(xmm2, xmm3); // rho* = Re(rho) - Im(rho) - - // Compute the different rhos - rho_rs[0] = simde_mm_mulhi_epi16(rho_rpi, ONE_OVER_SQRT_10); - rho_rs[4] = simde_mm_mulhi_epi16(rho_rmi, ONE_OVER_SQRT_10); - rho_rs[3] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(rho_rpi, THREE_OVER_SQRT_10), 1); - rho_rs[7] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(rho_rmi, THREE_OVER_SQRT_10), 1); - - xmm4 = simde_mm_mulhi_epi16(xmm2, ONE_OVER_SQRT_10); // Re(rho) - xmm5 = simde_mm_mulhi_epi16(xmm3, THREE_OVER_SQRT_10); // Im(rho) - xmm5 = simde_mm_slli_epi16(xmm5, 1); - - rho_rs[1] = simde_mm_adds_epi16(xmm4, xmm5); - rho_rs[5] = simde_mm_subs_epi16(xmm4, xmm5); - - xmm6 = simde_mm_mulhi_epi16(xmm2, THREE_OVER_SQRT_10); // Re(rho) - xmm7 = simde_mm_mulhi_epi16(xmm3, ONE_OVER_SQRT_10); // Im(rho) - xmm6 = simde_mm_slli_epi16(xmm6, 1); - - rho_rs[2] = simde_mm_adds_epi16(xmm6, xmm7); - rho_rs[6] = simde_mm_subs_epi16(xmm6, xmm7); - - // Rearrange interfering MF output - oai_mm_separate_real_imag_parts(&y1r, &y1i, stream1_128i_in[i], stream1_128i_in[i + 1]); - - // | [Re(rho)+ Im(rho)]/sqrt(10) - y1r | - for(int j=0; j<8; j++){ // psi_rs[0~7], rho_rs[0~7] - psi_rs[j] = simde_mm_abs_epi16( simde_mm_subs_epi16(rho_rs[j], y1r) ); - } - for(int j=8; j<16; j++){ // psi_rs[8~16], rho_rs[4,5,6,7,0,1,2,3] - psi_rs[j] = simde_mm_abs_epi16( simde_mm_adds_epi16(rho_rs[(j-4) & 7], y1r) ); - } - const uint8_t rho_rs_indexes[16] = {4,6,5,7,0,2,1,3,0,2,1,3,4,6,5,7}; - for(int k=0; k<16; k+=8){ // psi_is[0~15], sub(rho_rs[4,6,5,7]), add(rho_rs[0,2,1,3]), sub(rho_rs[0,2,1,3]), add(rho_rs[4,6,5,7]) - for(int j=k; j> 3; i += 2) { - - // Get rho - oai_mm256_separate_real_imag_parts(&xmm2, &xmm3, rho01_256i[i], rho01_256i[i + 1]); - rho_rpi = simde_mm256_adds_epi16(xmm2, xmm3); // rho = Re(rho) + Im(rho) - rho_rmi = simde_mm256_subs_epi16(xmm2, xmm3); // rho* = Re(rho) - Im(rho) - - // Compute the different rhos - rho_rs[0] = simde_mm256_mulhi_epi16(rho_rpi, ONE_OVER_SQRT_10); - rho_rs[4] = simde_mm256_mulhi_epi16(rho_rmi, ONE_OVER_SQRT_10); - rho_rs[3] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(rho_rpi, THREE_OVER_SQRT_10), 1); - rho_rs[7] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(rho_rmi, THREE_OVER_SQRT_10), 1); - - xmm4 = simde_mm256_mulhi_epi16(xmm2, ONE_OVER_SQRT_10); // Re(rho) - xmm5 = simde_mm256_mulhi_epi16(xmm3, THREE_OVER_SQRT_10); // Im(rho) - xmm5 = simde_mm256_slli_epi16(xmm5, 1); - - rho_rs[1] = simde_mm256_adds_epi16(xmm4, xmm5); - rho_rs[5] = simde_mm256_subs_epi16(xmm4, xmm5); - - xmm6 = simde_mm256_mulhi_epi16(xmm2, THREE_OVER_SQRT_10); // Re(rho) - xmm7 = simde_mm256_mulhi_epi16(xmm3, ONE_OVER_SQRT_10); // Im(rho) - xmm6 = simde_mm256_slli_epi16(xmm6, 1); - - rho_rs[2] = simde_mm256_adds_epi16(xmm6, xmm7); - rho_rs[6] = simde_mm256_subs_epi16(xmm6, xmm7); - - // Rearrange interfering MF output - oai_mm256_separate_real_imag_parts(&y1r, &y1i, stream1_256i_in[i], stream1_256i_in[i + 1]); - - // | [Re(rho)+ Im(rho)]/sqrt(10) - y1r | - for(int j=0; j<8; j++){ // psi_rs[0~7], rho_rs[0~7] - psi_rs[j] = simde_mm256_abs_epi16( simde_mm256_subs_epi16(rho_rs[j], y1r) ); - } - for(int j=8; j<16; j++){ // psi_rs[8~16], rho_rs[4,5,6,7,0,1,2,3] - psi_rs[j] = simde_mm256_abs_epi16( simde_mm256_adds_epi16(rho_rs[(j-4) & 7], y1r) ); - } - const uint8_t rho_rs_indexes[16] = {4,6,5,7,0,2,1,3,0,2,1,3,4,6,5,7}; - for(int k=0; k<16; k+=8){ // psi_is[0~15], sub(rho_rs[4,6,5,7]), add(rho_rs[0,2,1,3]), sub(rho_rs[0,2,1,3]), add(rho_rs[4,6,5,7]) - for(int j=k; j> 2; i += 2) { - - // Get rho - simde__m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8; - oai_mm_separate_real_imag_parts(&xmm2, &xmm3, rho01_128i[i], rho01_128i[i + 1]); - - simde__m128i rho_rpi = simde_mm_adds_epi16(xmm2, xmm3); // rho = Re(rho) + Im(rho) - simde__m128i rho_rmi = simde_mm_subs_epi16(xmm2, xmm3); // rho* = Re(rho) - Im(rho) - - // Compute the different rhos - simde__m128i rho_rs[32]; - rho_rs[27] = simde_mm_mulhi_epi16(rho_rpi, ONE_OVER_SQRT_42); - rho_rs[28] = simde_mm_mulhi_epi16(rho_rmi, ONE_OVER_SQRT_42); - rho_rs[18] = simde_mm_mulhi_epi16(rho_rpi, THREE_OVER_SQRT_42); - rho_rs[21] = simde_mm_mulhi_epi16(rho_rmi, THREE_OVER_SQRT_42); - rho_rs[9] = simde_mm_mulhi_epi16(rho_rpi, FIVE_OVER_SQRT_42); - rho_rs[14] = simde_mm_mulhi_epi16(rho_rmi, FIVE_OVER_SQRT_42); - rho_rs[0] = simde_mm_mulhi_epi16(rho_rpi, SEVEN_OVER_SQRT_42); - rho_rs[7] = simde_mm_mulhi_epi16(rho_rmi, SEVEN_OVER_SQRT_42); - - rho_rs[9] = simde_mm_slli_epi16(rho_rs[9], 1); - rho_rs[14] = simde_mm_slli_epi16(rho_rs[14], 1); - rho_rs[0] = simde_mm_slli_epi16(rho_rs[0], 2); - rho_rs[7] = simde_mm_slli_epi16(rho_rs[7], 2); - - xmm4 = simde_mm_mulhi_epi16(xmm2, ONE_OVER_SQRT_42); - xmm5 = simde_mm_mulhi_epi16(xmm3, ONE_OVER_SQRT_42); - xmm6 = simde_mm_mulhi_epi16(xmm3, THREE_OVER_SQRT_42); - xmm7 = simde_mm_mulhi_epi16(xmm3, FIVE_OVER_SQRT_42); - xmm8 = simde_mm_mulhi_epi16(xmm3, SEVEN_OVER_SQRT_42); - xmm7 = simde_mm_slli_epi16(xmm7, 1); - xmm8 = simde_mm_slli_epi16(xmm8, 2); - - rho_rs[26] = simde_mm_adds_epi16(xmm4, xmm6); - rho_rs[29] = simde_mm_subs_epi16(xmm4, xmm6); - rho_rs[25] = simde_mm_adds_epi16(xmm4, xmm7); - rho_rs[30] = simde_mm_subs_epi16(xmm4, xmm7); - rho_rs[24] = simde_mm_adds_epi16(xmm4, xmm8); - rho_rs[31] = simde_mm_subs_epi16(xmm4, xmm8); - - xmm4 = simde_mm_mulhi_epi16(xmm2, THREE_OVER_SQRT_42); - rho_rs[19] = simde_mm_adds_epi16(xmm4, xmm5); - rho_rs[20] = simde_mm_subs_epi16(xmm4, xmm5); - rho_rs[17] = simde_mm_adds_epi16(xmm4, xmm7); - rho_rs[22] = simde_mm_subs_epi16(xmm4, xmm7); - rho_rs[16] = simde_mm_adds_epi16(xmm4, xmm8); - rho_rs[23] = simde_mm_subs_epi16(xmm4, xmm8); - - xmm4 = simde_mm_mulhi_epi16(xmm2, FIVE_OVER_SQRT_42); - xmm4 = simde_mm_slli_epi16(xmm4, 1); - rho_rs[11] = simde_mm_adds_epi16(xmm4, xmm5); - rho_rs[12] = simde_mm_subs_epi16(xmm4, xmm5); - rho_rs[10] = simde_mm_adds_epi16(xmm4, xmm6); - rho_rs[13] = simde_mm_subs_epi16(xmm4, xmm6); - rho_rs[8] = simde_mm_adds_epi16(xmm4, xmm8); - rho_rs[15] = simde_mm_subs_epi16(xmm4, xmm8); - - xmm4 = simde_mm_mulhi_epi16(xmm2, SEVEN_OVER_SQRT_42); - xmm4 = simde_mm_slli_epi16(xmm4, 2); - rho_rs[3] = simde_mm_adds_epi16(xmm4, xmm5); - rho_rs[4] = simde_mm_subs_epi16(xmm4, xmm5); - rho_rs[2] = simde_mm_adds_epi16(xmm4, xmm6); - rho_rs[5] = simde_mm_subs_epi16(xmm4, xmm6); - rho_rs[1] = simde_mm_adds_epi16(xmm4, xmm7); - rho_rs[6] = simde_mm_subs_epi16(xmm4, xmm7); - - // Rearrange interfering MF output - simde__m128i y1r, y1i; - oai_mm_separate_real_imag_parts(&y1r, &y1i, stream1_128i_in[i], stream1_128i_in[i + 1]); - - // Psi_r calculation from rho_rpi or rho_rmi - xmm0 = simde_mm_set1_epi16(0); // ZERO for abs_pi16 - xmm2 = simde_mm_subs_epi16(rho_rs[0], y1r); - - simde__m128i psi_r_s[64]; - for(int j=0; j<32; j++) // psi_r_s[0~31], rho_rs[0~31] - psi_r_s[j] = simde_mm_abs_epi16(simde_mm_subs_epi16(rho_rs[j], y1r)); - for(int j=32; j<64; j++) // psi_r_s[32~64], rho_rs[31~0] - psi_r_s[j] = simde_mm_abs_epi16(simde_mm_adds_epi16(rho_rs[63 - j], y1r)); - - // simde__m128i psi_i calculation from rho_rpi or rho_rmi - simde__m128i psi_i_s[64]; - const uint8_t rho_rs_index[32] = {7,15,23,31,24,16,8,0,6,14,22,30,25,17,9,1,5,13,21,29,26,18,10,2,4,12,20,28,27,19,11,3}; - for(int k=0; k<32; k+=8){ // psi_i_s[0~31] - for(int j=k; j>3], y0i_seven_over_sqrt_21); - y0_s[j+1] = simde_mm_adds_epi16(y0r_over_s[j>>3], y0i_five_over_sqrt_21); - y0_s[j+2] = simde_mm_adds_epi16(y0r_over_s[j>>3], y0i_three_over_sqrt_21); - y0_s[j+3] = simde_mm_adds_epi16(y0r_over_s[j>>3], y0i_one_over_sqrt_21); - y0_s[j+4] = simde_mm_subs_epi16(y0r_over_s[j>>3], y0i_one_over_sqrt_21); - y0_s[j+5] = simde_mm_subs_epi16(y0r_over_s[j>>3], y0i_three_over_sqrt_21); - y0_s[j+6] = simde_mm_subs_epi16(y0r_over_s[j>>3], y0i_five_over_sqrt_21); - y0_s[j+7] = simde_mm_subs_epi16(y0r_over_s[j>>3], y0i_seven_over_sqrt_21); - } - - ch_mag_int_with_sigma2 = simde_mm_srai_epi16(ch_mag_int, 1); // *2 - two_ch_mag_int_with_sigma2 = ch_mag_int; // *4 - three_ch_mag_int_with_sigma2 = simde_mm_adds_epi16(ch_mag_int_with_sigma2, two_ch_mag_int_with_sigma2); // *6 - simde__m128i a_r_s[64]; - simde__m128i a_i_s[64]; - simde__m128i psi_a_s[64]; - simde__m128i a_sq_s[64]; - for(int j=0; j<64; j++){ - // Detection of interference term - a_r_s[j] = interference_abs_64qam_epi16(psi_r_s[j], ch_mag_int_with_sigma2, two_ch_mag_int_with_sigma2, three_ch_mag_int_with_sigma2, ONE_OVER_SQRT_2_42, THREE_OVER_SQRT_2_42, FIVE_OVER_SQRT_2_42, SEVEN_OVER_SQRT_2_42); - a_i_s[j] = interference_abs_64qam_epi16(psi_i_s[j], ch_mag_int_with_sigma2, two_ch_mag_int_with_sigma2, three_ch_mag_int_with_sigma2, ONE_OVER_SQRT_2_42, THREE_OVER_SQRT_2_42, FIVE_OVER_SQRT_2_42, SEVEN_OVER_SQRT_2_42); - - // Calculation of a group of two terms in the bit metric involving product of psi and interference - psi_a_s[j] = prodsum_psi_a_epi16(psi_r_s[j], a_r_s[j], psi_i_s[j], a_i_s[j]); - - // Multiply by sqrt(2) - psi_a_s[j] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(psi_a_s[j], ONE_OVER_SQRT_2), 2); - - // Calculation of a group of two terms in the bit metric involving squares of interference - a_sq_s[j] = square_a_64qam_epi16(a_r_s[j], a_i_s[j], ch_mag_int, SQRT_42_OVER_FOUR); - } - - // Computing different multiples of ||h0||^2 - simde__m128i ch_mag_with_sigma2[10]; - enum ch_mag_over_42with_sigma2_vals {mag2=0, mag10, mag26, mag18, mag34, mag58, mag50, mag74, mag98}; - // x=1, y=1 - ch_mag_with_sigma2[mag2] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, ONE_OVER_FOUR_SQRT_42), 1); - // x=1, y=3 - ch_mag_with_sigma2[mag10] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, FIVE_OVER_FOUR_SQRT_42), 1); - // x=1, x=5 - ch_mag_with_sigma2[mag26] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, THIRTEEN_OVER_FOUR_SQRT_42), 1); - // x=1, y=7 - ch_mag_with_sigma2[mag50] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, TWENTYFIVE_OVER_FOUR_SQRT_42), 1); - // x=3, y=3 - ch_mag_with_sigma2[mag18] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, NINE_OVER_FOUR_SQRT_42), 1); - // x=3, y=5 - ch_mag_with_sigma2[mag34] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, SEVENTEEN_OVER_FOUR_SQRT_42), 1); - // x=3, y=7 - ch_mag_with_sigma2[mag58] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, TWENTYNINE_OVER_FOUR_SQRT_42), 2); - // x=5, y=5 - ch_mag_with_sigma2[mag50] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, TWENTYFIVE_OVER_FOUR_SQRT_42), 1); - // x=5, y=7 - ch_mag_with_sigma2[mag74] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, THIRTYSEVEN_OVER_FOUR_SQRT_42), 2); - // x=7, y=7 - ch_mag_with_sigma2[mag98] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, FORTYNINE_OVER_FOUR_SQRT_42), 2); - - // Computing Metrics - simde__m128i bit_met_s[64]; - const enum ch_mag_over_42with_sigma2_vals table[] = { - mag98, mag74, mag58, mag50, mag50, mag58, mag74, mag98, mag74, mag50, mag34, mag26, mag26, mag34, mag50, mag74, - mag58, mag34, mag18, mag10, mag10, mag18, mag34, mag58, mag50, mag26, mag10, mag2, mag2, mag10, mag26, mag50}; - - for(int i=0; i<32; i++){ - const simde__m128i x = simde_mm_adds_epi16(simde_mm_subs_epi16(psi_a_s[i], a_sq_s[i]), y0_s[i]); - bit_met_s[i] = simde_mm_subs_epi16(x, ch_mag_with_sigma2[table[i]]); - } - for(int i=0; i<32; i++){ - const simde__m128i x = simde_mm_subs_epi16(simde_mm_subs_epi16(psi_a_s[32 + i], a_sq_s[32 + i]), y0_s[31 - i]); - bit_met_s[32 + i] = simde_mm_subs_epi16(x, ch_mag_with_sigma2[table[31 - i]]); - } - - // Detection for bits - simde__m128i logmax_den_re0; - simde__m128i logmax_num_re0; - // Detection for 1st bit - // bit = 1 - xmm0 = max_epi16(bit_met_s[56],bit_met_s[57],bit_met_s[58],bit_met_s[59],bit_met_s[60],bit_met_s[61],bit_met_s[62],bit_met_s[63]); - xmm1 = max_epi16(bit_met_s[48],bit_met_s[49],bit_met_s[50],bit_met_s[51],bit_met_s[52],bit_met_s[53],bit_met_s[54],bit_met_s[55]); - xmm2 = max_epi16(bit_met_s[40],bit_met_s[41],bit_met_s[42],bit_met_s[43],bit_met_s[44],bit_met_s[45],bit_met_s[46],bit_met_s[47]); - xmm3 = max_epi16(bit_met_s[32],bit_met_s[33],bit_met_s[34],bit_met_s[35],bit_met_s[36],bit_met_s[37],bit_met_s[38],bit_met_s[39]); - logmax_den_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); - - // bit = 0 - xmm0 = max_epi16(bit_met_s[0],bit_met_s[1],bit_met_s[2],bit_met_s[3],bit_met_s[4],bit_met_s[5],bit_met_s[6],bit_met_s[7]); - xmm1 = max_epi16(bit_met_s[8],bit_met_s[9],bit_met_s[10],bit_met_s[11],bit_met_s[12],bit_met_s[13],bit_met_s[14],bit_met_s[15]); - xmm2 = max_epi16(bit_met_s[16],bit_met_s[17],bit_met_s[18],bit_met_s[19],bit_met_s[20],bit_met_s[21],bit_met_s[22],bit_met_s[23]); - xmm3 = max_epi16(bit_met_s[24],bit_met_s[25],bit_met_s[26],bit_met_s[27],bit_met_s[28],bit_met_s[29],bit_met_s[30],bit_met_s[31]); - logmax_num_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); - - y0r = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); - - // Detection for 2nd bit - // bit = 1 - xmm0 = max_epi16(bit_met_s[4],bit_met_s[12],bit_met_s[20],bit_met_s[28],bit_met_s[36],bit_met_s[44],bit_met_s[52],bit_met_s[60]); - xmm1 = max_epi16(bit_met_s[5],bit_met_s[13],bit_met_s[21],bit_met_s[29],bit_met_s[37],bit_met_s[45],bit_met_s[53],bit_met_s[61]); - xmm2 = max_epi16(bit_met_s[6],bit_met_s[14],bit_met_s[22],bit_met_s[30],bit_met_s[38],bit_met_s[46],bit_met_s[54],bit_met_s[62]); - xmm3 = max_epi16(bit_met_s[7],bit_met_s[15],bit_met_s[23],bit_met_s[31],bit_met_s[39],bit_met_s[47],bit_met_s[55],bit_met_s[63]); - logmax_den_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); - - // bit = 0 - xmm0 = max_epi16(bit_met_s[3],bit_met_s[11],bit_met_s[19],bit_met_s[27],bit_met_s[35],bit_met_s[43],bit_met_s[51],bit_met_s[59]); - xmm1 = max_epi16(bit_met_s[2],bit_met_s[10],bit_met_s[18],bit_met_s[26],bit_met_s[34],bit_met_s[42],bit_met_s[50],bit_met_s[58]); - xmm2 = max_epi16(bit_met_s[1],bit_met_s[9],bit_met_s[17],bit_met_s[25],bit_met_s[33],bit_met_s[41],bit_met_s[49],bit_met_s[57]); - xmm3 = max_epi16(bit_met_s[0],bit_met_s[8],bit_met_s[16],bit_met_s[24],bit_met_s[32],bit_met_s[40],bit_met_s[48],bit_met_s[56]); - logmax_num_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); - - y1r = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); - - // Detection for 3rd bit - xmm0 = max_epi16(bit_met_s[63],bit_met_s[62],bit_met_s[61],bit_met_s[60],bit_met_s[59],bit_met_s[58],bit_met_s[57],bit_met_s[56]); - xmm1 = max_epi16(bit_met_s[55],bit_met_s[54],bit_met_s[53],bit_met_s[52],bit_met_s[51],bit_met_s[50],bit_met_s[49],bit_met_s[48]); - xmm2 = max_epi16(bit_met_s[15],bit_met_s[14],bit_met_s[13],bit_met_s[12],bit_met_s[11],bit_met_s[10],bit_met_s[9],bit_met_s[8]); - xmm3 = max_epi16(bit_met_s[7],bit_met_s[6],bit_met_s[5],bit_met_s[4],bit_met_s[3],bit_met_s[2],bit_met_s[1],bit_met_s[0]); - logmax_den_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); - - xmm0 = max_epi16(bit_met_s[47],bit_met_s[46],bit_met_s[45],bit_met_s[44],bit_met_s[43],bit_met_s[42],bit_met_s[41],bit_met_s[40]); - xmm1 = max_epi16(bit_met_s[39],bit_met_s[38],bit_met_s[37],bit_met_s[36],bit_met_s[35],bit_met_s[34],bit_met_s[33],bit_met_s[32]); - xmm2 = max_epi16(bit_met_s[31],bit_met_s[30],bit_met_s[29],bit_met_s[28],bit_met_s[27],bit_met_s[26],bit_met_s[25],bit_met_s[24]); - xmm3 = max_epi16(bit_met_s[23],bit_met_s[22],bit_met_s[21],bit_met_s[20],bit_met_s[19],bit_met_s[18],bit_met_s[17],bit_met_s[16]); - logmax_num_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); - - simde__m128i y2r = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); - - // Detection for 4th bit - xmm0 = max_epi16(bit_met_s[0],bit_met_s[8],bit_met_s[16],bit_met_s[24],bit_met_s[32],bit_met_s[40],bit_met_s[48],bit_met_s[56]); - xmm1 = max_epi16(bit_met_s[1],bit_met_s[9],bit_met_s[17],bit_met_s[25],bit_met_s[33],bit_met_s[41],bit_met_s[49],bit_met_s[57]); - xmm2 = max_epi16(bit_met_s[6],bit_met_s[14],bit_met_s[22],bit_met_s[30],bit_met_s[38],bit_met_s[46],bit_met_s[54],bit_met_s[62]); - xmm3 = max_epi16(bit_met_s[7],bit_met_s[15],bit_met_s[23],bit_met_s[31],bit_met_s[39],bit_met_s[47],bit_met_s[55],bit_met_s[63]); - logmax_den_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); - - xmm0 = max_epi16(bit_met_s[4],bit_met_s[12],bit_met_s[20],bit_met_s[28],bit_met_s[36],bit_met_s[44],bit_met_s[52],bit_met_s[60]); - xmm1 = max_epi16(bit_met_s[5],bit_met_s[13],bit_met_s[21],bit_met_s[29],bit_met_s[37],bit_met_s[45],bit_met_s[53],bit_met_s[61]); - xmm2 = max_epi16(bit_met_s[3],bit_met_s[11],bit_met_s[19],bit_met_s[27],bit_met_s[35],bit_met_s[43],bit_met_s[51],bit_met_s[59]); - xmm3 = max_epi16(bit_met_s[2],bit_met_s[10],bit_met_s[18],bit_met_s[26],bit_met_s[34],bit_met_s[42],bit_met_s[50],bit_met_s[58]); - logmax_num_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); - - y0i = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); - - // Detection for 5th bit - xmm0 = max_epi16(bit_met_s[63],bit_met_s[62],bit_met_s[61],bit_met_s[60],bit_met_s[59],bit_met_s[58],bit_met_s[57],bit_met_s[56]); - xmm1 = max_epi16(bit_met_s[39],bit_met_s[38],bit_met_s[37],bit_met_s[36],bit_met_s[35],bit_met_s[34],bit_met_s[33],bit_met_s[32]); - xmm2 = max_epi16(bit_met_s[31],bit_met_s[30],bit_met_s[29],bit_met_s[28],bit_met_s[27],bit_met_s[26],bit_met_s[25],bit_met_s[24]); - xmm3 = max_epi16(bit_met_s[7],bit_met_s[6],bit_met_s[5],bit_met_s[4],bit_met_s[3],bit_met_s[2],bit_met_s[1],bit_met_s[0]); - logmax_den_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); - - xmm0 = max_epi16(bit_met_s[55],bit_met_s[54],bit_met_s[53],bit_met_s[52],bit_met_s[51],bit_met_s[50],bit_met_s[49],bit_met_s[48]); - xmm1 = max_epi16(bit_met_s[47],bit_met_s[46],bit_met_s[45],bit_met_s[44],bit_met_s[43],bit_met_s[42],bit_met_s[41],bit_met_s[40]); - xmm2 = max_epi16(bit_met_s[23],bit_met_s[22],bit_met_s[21],bit_met_s[20],bit_met_s[19],bit_met_s[18],bit_met_s[17],bit_met_s[16]); - xmm3 = max_epi16(bit_met_s[15],bit_met_s[14],bit_met_s[13],bit_met_s[12],bit_met_s[11],bit_met_s[10],bit_met_s[9],bit_met_s[8]); - logmax_num_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); - - y1i = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); - - // Detection for 6th bit - xmm0 = max_epi16(bit_met_s[0],bit_met_s[8],bit_met_s[16],bit_met_s[24],bit_met_s[32],bit_met_s[40],bit_met_s[48],bit_met_s[56]); - xmm1 = max_epi16(bit_met_s[3],bit_met_s[11],bit_met_s[19],bit_met_s[27],bit_met_s[35],bit_met_s[43],bit_met_s[51],bit_met_s[59]); - xmm2 = max_epi16(bit_met_s[4],bit_met_s[12],bit_met_s[20],bit_met_s[28],bit_met_s[36],bit_met_s[44],bit_met_s[52],bit_met_s[60]); - xmm3 = max_epi16(bit_met_s[7],bit_met_s[15],bit_met_s[23],bit_met_s[31],bit_met_s[39],bit_met_s[47],bit_met_s[55],bit_met_s[63]); - logmax_den_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); - - xmm0 = max_epi16(bit_met_s[6],bit_met_s[14],bit_met_s[22],bit_met_s[30],bit_met_s[38],bit_met_s[46],bit_met_s[54],bit_met_s[62]); - xmm1 = max_epi16(bit_met_s[5],bit_met_s[13],bit_met_s[21],bit_met_s[29],bit_met_s[37],bit_met_s[45],bit_met_s[53],bit_met_s[61]); - xmm2 = max_epi16(bit_met_s[2],bit_met_s[10],bit_met_s[18],bit_met_s[26],bit_met_s[34],bit_met_s[42],bit_met_s[50],bit_met_s[58]); - xmm3 = max_epi16(bit_met_s[1],bit_met_s[9],bit_met_s[17],bit_met_s[25],bit_met_s[33],bit_met_s[41],bit_met_s[49],bit_met_s[57]); - logmax_num_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); - - simde__m128i y2i = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); - - // Map to output stream, difficult to do in SIMD since we have 6 16bit LLRs - for (int re = 0; re < 8; re++) { - *stream0_out++ = ((short *)&y0r)[re]; - *stream0_out++ = ((short *)&y1r)[re]; - *stream0_out++ = ((short *)&y2r)[re]; - *stream0_out++ = ((short *)&y0i)[re]; - *stream0_out++ = ((short *)&y1i)[re]; - *stream0_out++ = ((short *)&y2i)[re]; - } - } -#else - simde__m256i *rho01_256i = (simde__m256i *)rho01; - simde__m256i *stream0_256i_in = (simde__m256i *)stream0_in; - simde__m256i *stream1_256i_in = (simde__m256i *)stream1_in; - simde__m256i *ch_mag_256i = (simde__m256i *)ch_mag; - simde__m256i *ch_mag_256i_i = (simde__m256i *)ch_mag_i; - - simde__m256i ONE_OVER_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(10112)); // round(1/sqrt(42)*2^16) - simde__m256i THREE_OVER_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(30337)); // round(3/sqrt(42)*2^16) - simde__m256i FIVE_OVER_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(25281)); // round(5/sqrt(42)*2^15) - simde__m256i SEVEN_OVER_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(17697)); // round(7/sqrt(42)*2^14) Q2.14 - simde__m256i ONE_OVER_SQRT_2 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(23170)); // round(1/sqrt(2)*2^15) - simde__m256i ONE_OVER_SQRT_2_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(3575)); // round(1/sqrt(2*42)*2^15) - simde__m256i THREE_OVER_SQRT_2_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(10726)); // round(3/sqrt(2*42)*2^15) - simde__m256i FIVE_OVER_SQRT_2_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(17876)); // round(5/sqrt(2*42)*2^15) - simde__m256i SEVEN_OVER_SQRT_2_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(25027)); // round(7/sqrt(2*42)*2^15) - simde__m256i FORTYNINE_OVER_FOUR_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(30969)); // round(49/(4*sqrt(42))*2^14), Q2.14 - simde__m256i THIRTYSEVEN_OVER_FOUR_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(23385)); // round(37/(4*sqrt(42))*2^14), Q2.14 - simde__m256i TWENTYFIVE_OVER_FOUR_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(31601)); // round(25/(4*sqrt(42))*2^15) - simde__m256i TWENTYNINE_OVER_FOUR_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(18329)); // round(29/(4*sqrt(42))*2^15), Q2.14 - simde__m256i SEVENTEEN_OVER_FOUR_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(21489)); // round(17/(4*sqrt(42))*2^15) - simde__m256i NINE_OVER_FOUR_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(11376)); // round(9/(4*sqrt(42))*2^15) - simde__m256i THIRTEEN_OVER_FOUR_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(16433)); // round(13/(4*sqrt(42))*2^15) - simde__m256i FIVE_OVER_FOUR_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(6320)); // round(5/(4*sqrt(42))*2^15) - simde__m256i ONE_OVER_FOUR_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(1264)); // round(1/(4*sqrt(42))*2^15) - simde__m256i SQRT_42_OVER_FOUR = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(13272)); // round(sqrt(42)/4*2^13), Q3.12 - - simde__m256i ch_mag_des; - simde__m256i ch_mag_int; - simde__m256i y0r_one_over_sqrt_21; - simde__m256i y0r_three_over_sqrt_21; - simde__m256i y0r_five_over_sqrt_21; - simde__m256i y0r_seven_over_sqrt_21; - simde__m256i y0i_one_over_sqrt_21; - simde__m256i y0i_three_over_sqrt_21; - simde__m256i y0i_five_over_sqrt_21; - simde__m256i y0i_seven_over_sqrt_21; - simde__m256i ch_mag_int_with_sigma2; - simde__m256i two_ch_mag_int_with_sigma2; - simde__m256i three_ch_mag_int_with_sigma2; - - uint32_t len256 = length >> 3; - - for (int i = 0; i < len256; i += 2) { - - // Get rho - simde__m256i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8; - oai_mm256_separate_real_imag_parts(&xmm2, &xmm3, rho01_256i[i], rho01_256i[i + 1]); - - simde__m256i rho_rpi = simde_mm256_adds_epi16(xmm2, xmm3); // rho = Re(rho) + Im(rho) - simde__m256i rho_rmi = simde_mm256_subs_epi16(xmm2, xmm3); // rho* = Re(rho) - Im(rho) - - // Compute the different rhos - simde__m256i rho_rs[32]; - rho_rs[27] = simde_mm256_mulhi_epi16(rho_rpi, ONE_OVER_SQRT_42); - rho_rs[28] = simde_mm256_mulhi_epi16(rho_rmi, ONE_OVER_SQRT_42); - rho_rs[18] = simde_mm256_mulhi_epi16(rho_rpi, THREE_OVER_SQRT_42); - rho_rs[21] = simde_mm256_mulhi_epi16(rho_rmi, THREE_OVER_SQRT_42); - rho_rs[9] = simde_mm256_mulhi_epi16(rho_rpi, FIVE_OVER_SQRT_42); - rho_rs[14] = simde_mm256_mulhi_epi16(rho_rmi, FIVE_OVER_SQRT_42); - rho_rs[0] = simde_mm256_mulhi_epi16(rho_rpi, SEVEN_OVER_SQRT_42); - rho_rs[7] = simde_mm256_mulhi_epi16(rho_rmi, SEVEN_OVER_SQRT_42); - - rho_rs[9] = simde_mm256_slli_epi16(rho_rs[9], 1); - rho_rs[14] = simde_mm256_slli_epi16(rho_rs[14], 1); - rho_rs[0] = simde_mm256_slli_epi16(rho_rs[0], 2); - rho_rs[7] = simde_mm256_slli_epi16(rho_rs[7], 2); - - xmm4 = simde_mm256_mulhi_epi16(xmm2, ONE_OVER_SQRT_42); - xmm5 = simde_mm256_mulhi_epi16(xmm3, ONE_OVER_SQRT_42); - xmm6 = simde_mm256_mulhi_epi16(xmm3, THREE_OVER_SQRT_42); - xmm7 = simde_mm256_mulhi_epi16(xmm3, FIVE_OVER_SQRT_42); - xmm8 = simde_mm256_mulhi_epi16(xmm3, SEVEN_OVER_SQRT_42); - xmm7 = simde_mm256_slli_epi16(xmm7, 1); - xmm8 = simde_mm256_slli_epi16(xmm8, 2); - - rho_rs[26] = simde_mm256_adds_epi16(xmm4, xmm6); - rho_rs[29] = simde_mm256_subs_epi16(xmm4, xmm6); - rho_rs[25] = simde_mm256_adds_epi16(xmm4, xmm7); - rho_rs[30] = simde_mm256_subs_epi16(xmm4, xmm7); - rho_rs[24] = simde_mm256_adds_epi16(xmm4, xmm8); - rho_rs[31] = simde_mm256_subs_epi16(xmm4, xmm8); - - xmm4 = simde_mm256_mulhi_epi16(xmm2, THREE_OVER_SQRT_42); - rho_rs[19] = simde_mm256_adds_epi16(xmm4, xmm5); - rho_rs[20] = simde_mm256_subs_epi16(xmm4, xmm5); - rho_rs[17] = simde_mm256_adds_epi16(xmm4, xmm7); - rho_rs[22] = simde_mm256_subs_epi16(xmm4, xmm7); - rho_rs[16] = simde_mm256_adds_epi16(xmm4, xmm8); - rho_rs[23] = simde_mm256_subs_epi16(xmm4, xmm8); - - xmm4 = simde_mm256_mulhi_epi16(xmm2, FIVE_OVER_SQRT_42); - xmm4 = simde_mm256_slli_epi16(xmm4, 1); - rho_rs[11] = simde_mm256_adds_epi16(xmm4, xmm5); - rho_rs[12] = simde_mm256_subs_epi16(xmm4, xmm5); - rho_rs[10] = simde_mm256_adds_epi16(xmm4, xmm6); - rho_rs[13] = simde_mm256_subs_epi16(xmm4, xmm6); - rho_rs[8] = simde_mm256_adds_epi16(xmm4, xmm8); - rho_rs[15] = simde_mm256_subs_epi16(xmm4, xmm8); - - xmm4 = simde_mm256_mulhi_epi16(xmm2, SEVEN_OVER_SQRT_42); - xmm4 = simde_mm256_slli_epi16(xmm4, 2); - rho_rs[3] = simde_mm256_adds_epi16(xmm4, xmm5); - rho_rs[4] = simde_mm256_subs_epi16(xmm4, xmm5); - rho_rs[2] = simde_mm256_adds_epi16(xmm4, xmm6); - rho_rs[5] = simde_mm256_subs_epi16(xmm4, xmm6); - rho_rs[1] = simde_mm256_adds_epi16(xmm4, xmm7); - rho_rs[6] = simde_mm256_subs_epi16(xmm4, xmm7); - - // Rearrange interfering MF output - simde__m256i y1r, y1i; - oai_mm256_separate_real_imag_parts(&y1r, &y1i, stream1_256i_in[i], stream1_256i_in[i + 1]); - - // Psi_r calculation from rho_rpi or rho_rmi - xmm0 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(0)); // ZERO for abs_pi16 - xmm2 = simde_mm256_subs_epi16(rho_rs[0], y1r); - - simde__m256i psi_r_s[64]; - for(int j=0; j<32; j++) // psi_r_s[0~31], rho_rs[0~31] - psi_r_s[j] = simde_mm256_abs_epi16(simde_mm256_subs_epi16(rho_rs[j], y1r)); - for(int j=32; j<64; j++) // psi_r_s[32~64], rho_rs[31~0] - psi_r_s[j] = simde_mm256_abs_epi16(simde_mm256_adds_epi16(rho_rs[63 - j], y1r)); - - // simde__m256i psi_i calculation from rho_rpi or rho_rmi - simde__m256i psi_i_s[64]; - const uint8_t rho_rs_index[32] = {7,15,23,31,24,16,8,0,6,14,22,30,25,17,9,1,5,13,21,29,26,18,10,2,4,12,20,28,27,19,11,3}; - for(int k=0; k<32; k+=8){ // psi_i_s[0~31] - for(int j=k; j>3], y0i_seven_over_sqrt_21); - y0_s[j+1] = simde_mm256_adds_epi16(y0r_over_s[j>>3], y0i_five_over_sqrt_21); - y0_s[j+2] = simde_mm256_adds_epi16(y0r_over_s[j>>3], y0i_three_over_sqrt_21); - y0_s[j+3] = simde_mm256_adds_epi16(y0r_over_s[j>>3], y0i_one_over_sqrt_21); - y0_s[j+4] = simde_mm256_subs_epi16(y0r_over_s[j>>3], y0i_one_over_sqrt_21); - y0_s[j+5] = simde_mm256_subs_epi16(y0r_over_s[j>>3], y0i_three_over_sqrt_21); - y0_s[j+6] = simde_mm256_subs_epi16(y0r_over_s[j>>3], y0i_five_over_sqrt_21); - y0_s[j+7] = simde_mm256_subs_epi16(y0r_over_s[j>>3], y0i_seven_over_sqrt_21); - } - - ch_mag_int_with_sigma2 = simde_mm256_srai_epi16(ch_mag_int, 1); // *2 - two_ch_mag_int_with_sigma2 = ch_mag_int; // *4 - three_ch_mag_int_with_sigma2 = simde_mm256_adds_epi16(ch_mag_int_with_sigma2, two_ch_mag_int_with_sigma2); // *6 - simde__m256i a_r_s[64]; - simde__m256i a_i_s[64]; - simde__m256i psi_a_s[64]; - simde__m256i a_sq_s[64]; - for(int j=0; j<64; j++){ - // Detection of interference term - a_r_s[j] = interference_abs_64qam_epi16_256(psi_r_s[j], ch_mag_int_with_sigma2, two_ch_mag_int_with_sigma2, three_ch_mag_int_with_sigma2, ONE_OVER_SQRT_2_42, THREE_OVER_SQRT_2_42, FIVE_OVER_SQRT_2_42, SEVEN_OVER_SQRT_2_42); - a_i_s[j] = interference_abs_64qam_epi16_256(psi_i_s[j], ch_mag_int_with_sigma2, two_ch_mag_int_with_sigma2, three_ch_mag_int_with_sigma2, ONE_OVER_SQRT_2_42, THREE_OVER_SQRT_2_42, FIVE_OVER_SQRT_2_42, SEVEN_OVER_SQRT_2_42); - - // Calculation of a group of two terms in the bit metric involving product of psi and interference - psi_a_s[j] = prodsum_psi_a_epi16_256(psi_r_s[j], a_r_s[j], psi_i_s[j], a_i_s[j]); - - // Multiply by sqrt(2) - psi_a_s[j] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(psi_a_s[j], ONE_OVER_SQRT_2), 2); - - // Calculation of a group of two terms in the bit metric involving squares of interference - a_sq_s[j] = square_a_64qam_epi16_256(a_r_s[j], a_i_s[j], ch_mag_int, SQRT_42_OVER_FOUR); - } - - // Computing different multiples of ||h0||^2 - simde__m256i ch_mag_with_sigma2[10]; - enum ch_mag_over_42with_sigma2_vals {mag2=0, mag10, mag26, mag18, mag34, mag58, mag50, mag74, mag98}; - // x=1, y=1 - ch_mag_with_sigma2[mag2] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, ONE_OVER_FOUR_SQRT_42), 1); - // x=1, y=3 - ch_mag_with_sigma2[mag10] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, FIVE_OVER_FOUR_SQRT_42), 1); - // x=1, x=5 - ch_mag_with_sigma2[mag26] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, THIRTEEN_OVER_FOUR_SQRT_42), 1); - // x=1, y=7 - ch_mag_with_sigma2[mag50] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, TWENTYFIVE_OVER_FOUR_SQRT_42), 1); - // x=3, y=3 - ch_mag_with_sigma2[mag18] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, NINE_OVER_FOUR_SQRT_42), 1); - // x=3, y=5 - ch_mag_with_sigma2[mag34] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, SEVENTEEN_OVER_FOUR_SQRT_42), 1); - // x=3, y=7 - ch_mag_with_sigma2[mag58] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, TWENTYNINE_OVER_FOUR_SQRT_42), 2); - // x=5, y=5 - ch_mag_with_sigma2[mag50] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, TWENTYFIVE_OVER_FOUR_SQRT_42), 1); - // x=5, y=7 - ch_mag_with_sigma2[mag74] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, THIRTYSEVEN_OVER_FOUR_SQRT_42), 2); - // x=7, y=7 - ch_mag_with_sigma2[mag98] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, FORTYNINE_OVER_FOUR_SQRT_42), 2); - - // Computing Metrics - simde__m256i bit_met_s[64]; - const enum ch_mag_over_42with_sigma2_vals table[] = { - mag98, mag74, mag58, mag50, mag50, mag58, mag74, mag98, mag74, mag50, mag34, mag26, mag26, mag34, mag50, mag74, - mag58, mag34, mag18, mag10, mag10, mag18, mag34, mag58, mag50, mag26, mag10, mag2, mag2, mag10, mag26, mag50}; - - for(int i=0; i<32; i++){ - const simde__m256i x = simde_mm256_adds_epi16(simde_mm256_subs_epi16(psi_a_s[i], a_sq_s[i]), y0_s[i]); - bit_met_s[i] = simde_mm256_subs_epi16(x, ch_mag_with_sigma2[table[i]]); - } - for(int i=0; i<32; i++){ - const simde__m256i x = simde_mm256_subs_epi16(simde_mm256_subs_epi16(psi_a_s[32 + i], a_sq_s[32 + i]), y0_s[31 - i]); - bit_met_s[32 + i] = simde_mm256_subs_epi16(x, ch_mag_with_sigma2[table[31 - i]]); - } - - // Detection for bits - simde__m256i logmax_den_re0; - simde__m256i logmax_num_re0; - // Detection for 1st bit - // bit = 1 - xmm0 = max_epi16_256(bit_met_s[56],bit_met_s[57],bit_met_s[58],bit_met_s[59],bit_met_s[60],bit_met_s[61],bit_met_s[62],bit_met_s[63]); - xmm1 = max_epi16_256(bit_met_s[48],bit_met_s[49],bit_met_s[50],bit_met_s[51],bit_met_s[52],bit_met_s[53],bit_met_s[54],bit_met_s[55]); - xmm2 = max_epi16_256(bit_met_s[40],bit_met_s[41],bit_met_s[42],bit_met_s[43],bit_met_s[44],bit_met_s[45],bit_met_s[46],bit_met_s[47]); - xmm3 = max_epi16_256(bit_met_s[32],bit_met_s[33],bit_met_s[34],bit_met_s[35],bit_met_s[36],bit_met_s[37],bit_met_s[38],bit_met_s[39]); - logmax_den_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); - - // bit = 0 - xmm0 = max_epi16_256(bit_met_s[0],bit_met_s[1],bit_met_s[2],bit_met_s[3],bit_met_s[4],bit_met_s[5],bit_met_s[6],bit_met_s[7]); - xmm1 = max_epi16_256(bit_met_s[8],bit_met_s[9],bit_met_s[10],bit_met_s[11],bit_met_s[12],bit_met_s[13],bit_met_s[14],bit_met_s[15]); - xmm2 = max_epi16_256(bit_met_s[16],bit_met_s[17],bit_met_s[18],bit_met_s[19],bit_met_s[20],bit_met_s[21],bit_met_s[22],bit_met_s[23]); - xmm3 = max_epi16_256(bit_met_s[24],bit_met_s[25],bit_met_s[26],bit_met_s[27],bit_met_s[28],bit_met_s[29],bit_met_s[30],bit_met_s[31]); - logmax_num_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); - - y0r = simde_mm256_subs_epi16(logmax_num_re0, logmax_den_re0); - - // Detection for 2nd bit - // bit = 1 - xmm0 = max_epi16_256(bit_met_s[4],bit_met_s[12],bit_met_s[20],bit_met_s[28],bit_met_s[36],bit_met_s[44],bit_met_s[52],bit_met_s[60]); - xmm1 = max_epi16_256(bit_met_s[5],bit_met_s[13],bit_met_s[21],bit_met_s[29],bit_met_s[37],bit_met_s[45],bit_met_s[53],bit_met_s[61]); - xmm2 = max_epi16_256(bit_met_s[6],bit_met_s[14],bit_met_s[22],bit_met_s[30],bit_met_s[38],bit_met_s[46],bit_met_s[54],bit_met_s[62]); - xmm3 = max_epi16_256(bit_met_s[7],bit_met_s[15],bit_met_s[23],bit_met_s[31],bit_met_s[39],bit_met_s[47],bit_met_s[55],bit_met_s[63]); - logmax_den_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); - - // bit = 0 - xmm0 = max_epi16_256(bit_met_s[3],bit_met_s[11],bit_met_s[19],bit_met_s[27],bit_met_s[35],bit_met_s[43],bit_met_s[51],bit_met_s[59]); - xmm1 = max_epi16_256(bit_met_s[2],bit_met_s[10],bit_met_s[18],bit_met_s[26],bit_met_s[34],bit_met_s[42],bit_met_s[50],bit_met_s[58]); - xmm2 = max_epi16_256(bit_met_s[1],bit_met_s[9],bit_met_s[17],bit_met_s[25],bit_met_s[33],bit_met_s[41],bit_met_s[49],bit_met_s[57]); - xmm3 = max_epi16_256(bit_met_s[0],bit_met_s[8],bit_met_s[16],bit_met_s[24],bit_met_s[32],bit_met_s[40],bit_met_s[48],bit_met_s[56]); - logmax_num_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); - - y1r = simde_mm256_subs_epi16(logmax_num_re0, logmax_den_re0); - - // Detection for 3rd bit - xmm0 = max_epi16_256(bit_met_s[63],bit_met_s[62],bit_met_s[61],bit_met_s[60],bit_met_s[59],bit_met_s[58],bit_met_s[57],bit_met_s[56]); - xmm1 = max_epi16_256(bit_met_s[55],bit_met_s[54],bit_met_s[53],bit_met_s[52],bit_met_s[51],bit_met_s[50],bit_met_s[49],bit_met_s[48]); - xmm2 = max_epi16_256(bit_met_s[15],bit_met_s[14],bit_met_s[13],bit_met_s[12],bit_met_s[11],bit_met_s[10],bit_met_s[9],bit_met_s[8]); - xmm3 = max_epi16_256(bit_met_s[7],bit_met_s[6],bit_met_s[5],bit_met_s[4],bit_met_s[3],bit_met_s[2],bit_met_s[1],bit_met_s[0]); - logmax_den_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); - - xmm0 = max_epi16_256(bit_met_s[47],bit_met_s[46],bit_met_s[45],bit_met_s[44],bit_met_s[43],bit_met_s[42],bit_met_s[41],bit_met_s[40]); - xmm1 = max_epi16_256(bit_met_s[39],bit_met_s[38],bit_met_s[37],bit_met_s[36],bit_met_s[35],bit_met_s[34],bit_met_s[33],bit_met_s[32]); - xmm2 = max_epi16_256(bit_met_s[31],bit_met_s[30],bit_met_s[29],bit_met_s[28],bit_met_s[27],bit_met_s[26],bit_met_s[25],bit_met_s[24]); - xmm3 = max_epi16_256(bit_met_s[23],bit_met_s[22],bit_met_s[21],bit_met_s[20],bit_met_s[19],bit_met_s[18],bit_met_s[17],bit_met_s[16]); - logmax_num_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); - - simde__m256i y2r = simde_mm256_subs_epi16(logmax_num_re0, logmax_den_re0); - - // Detection for 4th bit - xmm0 = max_epi16_256(bit_met_s[0],bit_met_s[8],bit_met_s[16],bit_met_s[24],bit_met_s[32],bit_met_s[40],bit_met_s[48],bit_met_s[56]); - xmm1 = max_epi16_256(bit_met_s[1],bit_met_s[9],bit_met_s[17],bit_met_s[25],bit_met_s[33],bit_met_s[41],bit_met_s[49],bit_met_s[57]); - xmm2 = max_epi16_256(bit_met_s[6],bit_met_s[14],bit_met_s[22],bit_met_s[30],bit_met_s[38],bit_met_s[46],bit_met_s[54],bit_met_s[62]); - xmm3 = max_epi16_256(bit_met_s[7],bit_met_s[15],bit_met_s[23],bit_met_s[31],bit_met_s[39],bit_met_s[47],bit_met_s[55],bit_met_s[63]); - logmax_den_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); - - xmm0 = max_epi16_256(bit_met_s[4],bit_met_s[12],bit_met_s[20],bit_met_s[28],bit_met_s[36],bit_met_s[44],bit_met_s[52],bit_met_s[60]); - xmm1 = max_epi16_256(bit_met_s[5],bit_met_s[13],bit_met_s[21],bit_met_s[29],bit_met_s[37],bit_met_s[45],bit_met_s[53],bit_met_s[61]); - xmm2 = max_epi16_256(bit_met_s[3],bit_met_s[11],bit_met_s[19],bit_met_s[27],bit_met_s[35],bit_met_s[43],bit_met_s[51],bit_met_s[59]); - xmm3 = max_epi16_256(bit_met_s[2],bit_met_s[10],bit_met_s[18],bit_met_s[26],bit_met_s[34],bit_met_s[42],bit_met_s[50],bit_met_s[58]); - logmax_num_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); - - y0i = simde_mm256_subs_epi16(logmax_num_re0, logmax_den_re0); - - // Detection for 5th bit - xmm0 = max_epi16_256(bit_met_s[63],bit_met_s[62],bit_met_s[61],bit_met_s[60],bit_met_s[59],bit_met_s[58],bit_met_s[57],bit_met_s[56]); - xmm1 = max_epi16_256(bit_met_s[39],bit_met_s[38],bit_met_s[37],bit_met_s[36],bit_met_s[35],bit_met_s[34],bit_met_s[33],bit_met_s[32]); - xmm2 = max_epi16_256(bit_met_s[31],bit_met_s[30],bit_met_s[29],bit_met_s[28],bit_met_s[27],bit_met_s[26],bit_met_s[25],bit_met_s[24]); - xmm3 = max_epi16_256(bit_met_s[7],bit_met_s[6],bit_met_s[5],bit_met_s[4],bit_met_s[3],bit_met_s[2],bit_met_s[1],bit_met_s[0]); - logmax_den_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); - - xmm0 = max_epi16_256(bit_met_s[55],bit_met_s[54],bit_met_s[53],bit_met_s[52],bit_met_s[51],bit_met_s[50],bit_met_s[49],bit_met_s[48]); - xmm1 = max_epi16_256(bit_met_s[47],bit_met_s[46],bit_met_s[45],bit_met_s[44],bit_met_s[43],bit_met_s[42],bit_met_s[41],bit_met_s[40]); - xmm2 = max_epi16_256(bit_met_s[23],bit_met_s[22],bit_met_s[21],bit_met_s[20],bit_met_s[19],bit_met_s[18],bit_met_s[17],bit_met_s[16]); - xmm3 = max_epi16_256(bit_met_s[15],bit_met_s[14],bit_met_s[13],bit_met_s[12],bit_met_s[11],bit_met_s[10],bit_met_s[9],bit_met_s[8]); - logmax_num_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); - - y1i = simde_mm256_subs_epi16(logmax_num_re0, logmax_den_re0); - - // Detection for 6th bit - xmm0 = max_epi16_256(bit_met_s[0],bit_met_s[8],bit_met_s[16],bit_met_s[24],bit_met_s[32],bit_met_s[40],bit_met_s[48],bit_met_s[56]); - xmm1 = max_epi16_256(bit_met_s[3],bit_met_s[11],bit_met_s[19],bit_met_s[27],bit_met_s[35],bit_met_s[43],bit_met_s[51],bit_met_s[59]); - xmm2 = max_epi16_256(bit_met_s[4],bit_met_s[12],bit_met_s[20],bit_met_s[28],bit_met_s[36],bit_met_s[44],bit_met_s[52],bit_met_s[60]); - xmm3 = max_epi16_256(bit_met_s[7],bit_met_s[15],bit_met_s[23],bit_met_s[31],bit_met_s[39],bit_met_s[47],bit_met_s[55],bit_met_s[63]); - logmax_den_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); - - xmm0 = max_epi16_256(bit_met_s[6],bit_met_s[14],bit_met_s[22],bit_met_s[30],bit_met_s[38],bit_met_s[46],bit_met_s[54],bit_met_s[62]); - xmm1 = max_epi16_256(bit_met_s[5],bit_met_s[13],bit_met_s[21],bit_met_s[29],bit_met_s[37],bit_met_s[45],bit_met_s[53],bit_met_s[61]); - xmm2 = max_epi16_256(bit_met_s[2],bit_met_s[10],bit_met_s[18],bit_met_s[26],bit_met_s[34],bit_met_s[42],bit_met_s[50],bit_met_s[58]); - xmm3 = max_epi16_256(bit_met_s[1],bit_met_s[9],bit_met_s[17],bit_met_s[25],bit_met_s[33],bit_met_s[41],bit_met_s[49],bit_met_s[57]); - logmax_num_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); - - simde__m256i y2i = simde_mm256_subs_epi16(logmax_num_re0, logmax_den_re0); - - // Map to output stream, difficult to do in SIMD since we have 6 16bit LLRs - for (int re = 0; re < 16; re++) { - *stream0_out++ = ((short *)&y0r)[re]; - *stream0_out++ = ((short *)&y1r)[re]; - *stream0_out++ = ((short *)&y2r)[re]; - *stream0_out++ = ((short *)&y0i)[re]; - *stream0_out++ = ((short *)&y1i)[re]; - *stream0_out++ = ((short *)&y2i)[re]; - } - } -#endif -} - -static void nr_ulsch_shift_llr(int16_t *llr_layer0, int16_t *llr_layer1, uint32_t nb_re, int shift) -{ - simde__m128i *llr_layers0 = (simde__m128i *)llr_layer0; - simde__m128i *llr_layers1 = (simde__m128i *)llr_layer1; - - uint8_t mem_offset = ((16 - ((long)llr_layers0)) & 0xF) >> 2; - - if (mem_offset > 0) { - c16_t *llr_layers0_c16 = (c16_t *)llr_layer0; - c16_t *llr_layers1_c16 = (c16_t *)llr_layer1; - for (int i = 0; i < mem_offset; i++) { - llr_layers0_c16[i] = c16Shift(llr_layers0_c16[i], shift); - llr_layers1_c16[i] = c16Shift(llr_layers1_c16[i], shift); - } - llr_layers0 = (simde__m128i *)&llr_layer0[mem_offset * 2]; - llr_layers1 = (simde__m128i *)&llr_layer1[mem_offset * 2]; - } - - for (int i = 0; i < nb_re >> 2; i++) { - llr_layers0[i] = simde_mm_srai_epi16(llr_layers0[i], shift); - llr_layers1[i] = simde_mm_srai_epi16(llr_layers1[i], shift); - } -} - -void nr_ulsch_compute_ML_llr(c16_t *rxdataF_comp0, - c16_t *rxdataF_comp1, - c16_t *ul_ch_mag0, - c16_t *ul_ch_mag1, - int16_t *llr_layers0, - int16_t *llr_layers1, - c16_t *rho0, - c16_t *rho1, - uint32_t nb_re, - uint8_t mod_order) -{ - switch (mod_order) { - case 2: - nr_ulsch_qpsk_qpsk(rxdataF_comp0, rxdataF_comp1, llr_layers0, rho0, nb_re); - nr_ulsch_qpsk_qpsk(rxdataF_comp1, rxdataF_comp0, llr_layers1, rho1, nb_re); - nr_ulsch_shift_llr((int16_t *)llr_layers0, (int16_t *)llr_layers1, nb_re, 4); - break; - case 4: - nr_ulsch_qam16_qam16(rxdataF_comp0, rxdataF_comp1, ul_ch_mag0, ul_ch_mag1, llr_layers0, rho0, nb_re); - nr_ulsch_qam16_qam16(rxdataF_comp1, rxdataF_comp0, ul_ch_mag1, ul_ch_mag0, llr_layers1, rho1, nb_re); - break; - case 6: - nr_ulsch_qam64_qam64(rxdataF_comp0, rxdataF_comp1, ul_ch_mag0, ul_ch_mag1, llr_layers0, rho0, nb_re); - nr_ulsch_qam64_qam64(rxdataF_comp1, rxdataF_comp0, ul_ch_mag1, ul_ch_mag0, llr_layers1, rho1, nb_re); - break; - default: - AssertFatal(1 == 0, "nr_ulsch_compute_llr: invalid Qm value, Qm = %d\n", mod_order); - } -} diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c index 673a0bbe8e..bf681baa97 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c @@ -782,7 +782,8 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, c16_t ptrs_phase_per_slot[][NR_SYMBOLS_PER_SLOT], int32_t ptrs_re_per_slot[][NR_SYMBOLS_PER_SLOT], uint32_t nvar, - pdsch_scope_req_t *scope_req) + pdsch_scope_req_t *scope_req, + c16_t rho_dl[][dlsch->cw_info.Nl * dlsch->cw_info.Nl][rx_size_symbol]) { NR_DL_FRAME_PARMS *fp = &ue->frame_parms; const int nl = dlsch->cw_info.Nl; @@ -830,7 +831,7 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, uint8_t pilots = (dlsch_config->dlDmrsSymbPos >> symbol) & 1; uint8_t config_type = dlsch_config->dmrsConfigType; - const bool need_rho __attribute__((unused)) = do_ml ? (nl == 2 && dlsch_config->cw_info->qamModOrder <= 6) : false; + const bool need_rho = do_ml ? (nl == 2 && dlsch_config->cw_info->qamModOrder <= 6) : false; //---------------------------------------------------------- //--------------------- RBs extraction --------------------- @@ -1001,7 +1002,7 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, dl_ch_magb[symbol], dl_ch_magr[symbol], p_rxComp, - NULL, + need_rho ? (c16_t(*)[nl][rx_size_symbol])rho_dl[symbol] : NULL, dlsch->cw_info.qamModOrder, 0, // symbol already baked into p_rxComp *log2_maxh); @@ -1039,20 +1040,40 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, // MRC is performed inline by nr_channel_compensation; apply MMSE for multi-layer start_meas_nr_ue_phy(ue, DLSCH_MRC_MMSE_STATS); - if (nl >= 2 && nb_re_pdsch) - nr_dlsch_mmse(rx_size_symbol, - nbRx, - nl, - rxdataF_comp, - dl_ch_mag[symbol], - dl_ch_magb[symbol], - dl_ch_magr[symbol], - dl_ch_estimates_ext, - dlsch->cw_info.qamModOrder, - *log2_maxh, - symbol, - nb_re_pdsch, - nvar); + if (nb_re_pdsch) { + const uint8_t qamModOrder = dlsch->cw_info.qamModOrder; + + if ((nl > 2) || (nl == 2 && !do_ml)) { + nr_dlsch_mmse(rx_size_symbol, + nbRx, + nl, + rxdataF_comp, + dl_ch_mag[symbol], + dl_ch_magb[symbol], + dl_ch_magr[symbol], + dl_ch_estimates_ext, + qamModOrder, + *log2_maxh, + symbol, + nb_re_pdsch, + nvar); + } else if ((nl == 2) && (qamModOrder > 6) && do_ml) { + nr_mmse_2layers(p_rxComp, + rx_size_symbol, + nbRx, + nl, + dl_ch_mag[symbol], + dl_ch_magb[symbol], + dl_ch_magr[symbol], + chFext, + freq_alloc->num_rbs, + qamModOrder, + *log2_maxh, + 0, + nb_re_pdsch, + nvar); + } + } stop_meas_nr_ue_phy(ue, DLSCH_MRC_MMSE_STATS); if (meas_enabled) { @@ -1105,18 +1126,35 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, __attribute__((aligned(32))) int16_t layer_llr[NR_SYMBOLS_PER_SLOT][nl][llr_per_symbol]; // Generate LLR from PTRS compensated signal + const uint8_t qamModOrder = dlsch->cw_info.qamModOrder; start_meas_nr_ue_phy(ue, DLSCH_LLR_STATS); for (int llr_sym = startSymbIdx; llr_sym < startSymbIdx + nbSymb; llr_sym++) { - nr_dlsch_llr(dlsch, - dl_valid_re[llr_sym], - rx_size_symbol, - dl_ch_mag[llr_sym][0], - dl_ch_magb[llr_sym][0], - dl_ch_magr[llr_sym][0], - nbRx, - rxdataF_comp[llr_sym], - llr_per_symbol, - layer_llr[llr_sym]); + if (nl == 2 && qamModOrder <= 6 && do_ml) { + // 2-layer QPSK/16QAM/64QAM: joint ML-LLR using inter-layer Tx correlation + // rho_dl[llr_sym] is laid out as [nl*nl][rx_size_symbol]: + // index 1 = rho[0][1], index nl (=2) = rho[1][0] + nr_compute_ML_llr(rxdataF_comp[llr_sym][0], + rxdataF_comp[llr_sym][nbRx], + dl_ch_mag[llr_sym][0], + dl_ch_mag[llr_sym][1], + layer_llr[llr_sym][0], + layer_llr[llr_sym][1], + rho_dl[llr_sym][1], + rho_dl[llr_sym][nl], + dl_valid_re[llr_sym], + qamModOrder); + } else { + nr_dlsch_llr(dlsch, + dl_valid_re[llr_sym], + rx_size_symbol, + dl_ch_mag[llr_sym][0], + dl_ch_magb[llr_sym][0], + dl_ch_magr[llr_sym][0], + nbRx, + rxdataF_comp[llr_sym], + llr_per_symbol, + layer_llr[llr_sym]); + } } stop_meas_nr_ue_phy(ue, DLSCH_LLR_STATS); start_meas_nr_ue_phy(ue, DLSCH_LAYER_DEMAPPING); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index 05a81969fc..dc869655a5 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -284,7 +284,8 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, c16_t ptrs_phase_per_slot[][NR_SYMBOLS_PER_SLOT], int32_t ptrs_re_per_slot[][NR_SYMBOLS_PER_SLOT], uint32_t nvar, - pdsch_scope_req_t *scope_req); + pdsch_scope_req_t *scope_req, + c16_t rho_dl[][dlsch->cw_info.Nl * dlsch->cw_info.Nl][rx_size_symbol]); int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t slot, c16_t **txData); void apply_ntn_config(PHY_VARS_NR_UE *UE, diff --git a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h index b3b96ad2d7..88ace17b88 100644 --- a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h +++ b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h @@ -413,4 +413,57 @@ void nr_channel_compensation(uint32_t buffer_length, int mod_order, uint32_t symbol, uint32_t output_shift); + +void nr_compute_llr(c16_t *rxdataF_comp, + c16_t *ch_mag, + c16_t *ch_magb, + c16_t *ch_magc, + int16_t *llr, + uint32_t nb_re, + uint8_t symbol, + uint8_t mod_order); + +void nr_qpsk_llr_2layer(c16_t *stream0_in, c16_t *stream1_in, int16_t *stream0_out, c16_t *rho01, uint32_t length); + +void nr_qam16_llr_2layer(c16_t *stream0_in, + c16_t *stream1_in, + c16_t *ch_mag, + c16_t *ch_mag_i, + int16_t *stream0_out, + c16_t *rho01, + uint32_t length); + +void nr_qam64_llr_2layer(c16_t *stream0_in, + c16_t *stream1_in, + c16_t *ch_mag, + c16_t *ch_mag_i, + int16_t *stream0_out, + c16_t *rho01, + uint32_t length); + +void nr_compute_ML_llr(c16_t *rxdataF_comp0, + c16_t *rxdataF_comp1, + c16_t *ch_mag0, + c16_t *ch_mag1, + int16_t *llr_layers0, + int16_t *llr_layers1, + c16_t *rho0, + c16_t *rho1, + uint32_t nb_re, + uint8_t mod_order); + +uint8_t nr_mmse_2layers(c16_t **rxdataF_comp, + uint32_t buffer_length, + int nb_rx_ant, + int nb_layers, + c16_t ch_mag[nb_layers][buffer_length], + c16_t ch_magb[nb_layers][buffer_length], + c16_t ch_magc[nb_layers][buffer_length], + c16_t ul_ch_estimates_ext[][nb_rx_ant][buffer_length], + unsigned short nb_rb, + unsigned char mod_order, + int shift, + unsigned char symbol, + int length, + uint32_t noise_var); #endif diff --git a/openair1/PHY/nr_phy_common/src/nr_phy_common.c b/openair1/PHY/nr_phy_common/src/nr_phy_common.c index b87e0a323a..8c8bc16620 100644 --- a/openair1/PHY/nr_phy_common/src/nr_phy_common.c +++ b/openair1/PHY/nr_phy_common/src/nr_phy_common.c @@ -592,3 +592,3060 @@ void nr_channel_compensation(uint32_t buffer_length, } } } + +void nr_compute_llr(c16_t *rxdataF_comp, + c16_t *ch_mag, + c16_t *ch_magb, + c16_t *ch_magc, + int16_t *llr, + uint32_t nb_re, + uint8_t symbol, + uint8_t mod_order) +{ + switch (mod_order) { + case 2: + nr_qpsk_llr(rxdataF_comp, llr, nb_re); + break; + case 4: + nr_16qam_llr(rxdataF_comp, ch_mag, llr, nb_re); + break; + case 6: + nr_64qam_llr(rxdataF_comp, ch_mag, ch_magb, llr, nb_re); + break; + case 8: + nr_256qam_llr(rxdataF_comp, ch_mag, ch_magb, ch_magc, llr, nb_re); + break; + default: + AssertFatal(false, "nr_compute_llr: invalid mod_order, symbol=%d, Qm=%d\n", symbol, mod_order); + break; + } +} + +/* + * This function computes the LLRs of stream 0 (s_0) in presence of the interfering stream 1 (s_1) assuming that both symbols are + * QPSK. It can be used for both MU-MIMO interference-aware receiver or for SU-MIMO receivers. + * + * Input: + * stream0_in: MF filter output for 1st stream, i.e., y0' = h0'*y0 + * stream1_in: MF filter output for 2nd stream, i.e., y1' = h1'*y0 + * rho01: Channel cross correlation, i.e., rho01 = h0'*h1 + * length: Number of resource elements + * + * Output: + * stream0_out: Output LLRs for 1st stream + */ +void nr_qpsk_llr_2layer(c16_t *stream0_in, c16_t *stream1_in, int16_t *stream0_out, c16_t *rho01, uint32_t length) +{ +#ifdef USE_128BIT + simde__m128i *rho01_128i = (simde__m128i *)rho01; + simde__m128i *stream0_128i_in = (simde__m128i *)stream0_in; + simde__m128i *stream1_128i_in = (simde__m128i *)stream1_in; + simde__m128i *stream0_128i_out = (simde__m128i *)stream0_out; + simde__m128i ONE_OVER_2_SQRT_2 = simde_mm_set1_epi16(23170); // round(2 ^ 16 / (2 * sqrt(2))) + + // In each iteration, we take 8 complex symbols + for (int i = 0; i < length >> 2; i += 2) { + /// Compute real and imaginary parts of MF output for stream 0 (desired stream) + simde__m128i y0r, y0i; + oai_mm_separate_real_imag_parts(&y0r, &y0i, stream0_128i_in[i], stream0_128i_in[i + 1]); + simde__m128i y0r_over2 = simde_mm_mulhi_epi16(y0r, ONE_OVER_2_SQRT_2); + y0r_over2 = simde_mm_slli_epi16(y0r_over2, 1); // y0r_over2 = Re(y0) / sqrt(2) + simde__m128i y0i_over2 = simde_mm_mulhi_epi16(y0i, ONE_OVER_2_SQRT_2); + y0i_over2 = simde_mm_slli_epi16(y0i_over2, 1); // y0i_over2 = Im(y0) / sqrt(2) + + /// Compute real and imaginary parts of MF output for stream 1 (interference stream) + simde__m128i y1r_over2, y1i_over2; + oai_mm_separate_real_imag_parts(&y1r_over2, &y1i_over2, stream1_128i_in[i], stream1_128i_in[i + 1]); + y1r_over2 = simde_mm_srai_epi16(y1r_over2, 1); // y1r_over2 = Re(y1) / 2 + y1i_over2 = simde_mm_srai_epi16(y1i_over2, 1); // y1i_over2 = Im(y1) / 2 + + /// Get real and imaginary parts of rho + simde__m128i rhor, rhoi; + oai_mm_separate_real_imag_parts(&rhor, &rhoi, rho01_128i[i], rho01_128i[i + 1]); + + /// Compute |psi_r| and |psi_i| + + // psi_r = rhor * xR + rhoi * xI + // psi_i = rhor * xI - rhoi * xR + + // Put (rho_r + rho_i)/(2*sqrt(2)) in rho_p + // rhor * xR + rhoi * xI --> xR = 1/sqrt(2) and xI = 1/sqrt(2) + // rhor * xI - rhoi * xR --> xR = -1/sqrt(2) and xI = 1/sqrt(2) + simde__m128i rho_p = simde_mm_adds_epi16(rhor, rhoi); // rho_p = Re(rho) + Im(rho) + rho_p = simde_mm_mulhi_epi16(rho_p, ONE_OVER_2_SQRT_2); // rho_p = rho_p / (2*sqrt(2)) + + // Put (rho_r - rho_i)/(2*sqrt(2)) in rho_m + // rhor * xR + rhoi * xI --> xR = 1/sqrt(2) and xI = -1/sqrt(2) + // rhor * xI - rhoi * xR --> xR = 1/sqrt(2) and xI = 1/sqrt(2) + simde__m128i rho_m = simde_mm_subs_epi16(rhor, rhoi); // rho_m = Re(rho) - Im(rho) + rho_m = simde_mm_mulhi_epi16(rho_m, ONE_OVER_2_SQRT_2); // rho_m = rho_m / (2*sqrt(2)) + + // xR = 1/sqrt(2) and xI = 1/sqrt(2) + simde__m128i abs_psi_rpm = simde_mm_subs_epi16(rho_p, y1r_over2); // psi_rpm = rho_p - y1r/2 + abs_psi_rpm = simde_mm_abs_epi16(abs_psi_rpm); // abs_psi_rpm = |psi_rpm| + + // xR = 1/sqrt(2) and xI = 1/sqrt(2) + simde__m128i abs_psi_imm = simde_mm_subs_epi16(rho_m, y1i_over2); // psi_imm = rho_m - y1i/2 + abs_psi_imm = simde_mm_abs_epi16(abs_psi_imm); // abs_psi_imm = |psi_imm| + + // xR = 1/sqrt(2) and xI = -1/sqrt(2) + simde__m128i abs_psi_rmm = simde_mm_subs_epi16(rho_m, y1r_over2); // psi_rmm = rho_m - y1r/2 + abs_psi_rmm = simde_mm_abs_epi16(abs_psi_rmm); // abs_psi_rmm = |psi_rmm| + + // xR = -1/sqrt(2) and xI = 1/sqrt(2) + simde__m128i abs_psi_ipm = simde_mm_subs_epi16(rho_p, y1i_over2); // psi_ipm = rho_p - y1i/2 + abs_psi_ipm = simde_mm_abs_epi16(abs_psi_ipm); // abs_psi_ipm = |psi_ipm| + + // xR = -1/sqrt(2) and xI = -1/sqrt(2) + simde__m128i abs_psi_rpp = simde_mm_adds_epi16(rho_p, y1r_over2); // psi_rpp = rho_p + y1r/2 + abs_psi_rpp = simde_mm_abs_epi16(abs_psi_rpp); // abs_psi_rpp = |psi_rpp| + + // xR = -1/sqrt(2) and xI = -1/sqrt(2) + simde__m128i abs_psi_imp = simde_mm_adds_epi16(rho_m, y1i_over2); // psi_imp = rho_m + y1i/2 + abs_psi_imp = simde_mm_abs_epi16(abs_psi_imp); // abs_psi_imp = |psi_imp| + + // xR = -1/sqrt(2) and xI = 1/sqrt(2) + simde__m128i abs_psi_rmp = simde_mm_adds_epi16(rho_m, y1r_over2); // psi_rmp = rho_m + y1r/2 + abs_psi_rmp = simde_mm_abs_epi16(abs_psi_rmp); // abs_psi_rmp = |psi_rmp| + + // xR = 1/sqrt(2) and xI = -1/sqrt(2) + simde__m128i abs_psi_ipp = simde_mm_adds_epi16(rho_p, y1i_over2); // psi_ipm = rho_p + y1i/2 + abs_psi_ipp = simde_mm_abs_epi16(abs_psi_ipp); // abs_psi_ipp = |psi_ipm| + + /// Compute bit metrics (lambda) + + // lambda = max { |psi_r - y1r| * |x2R| + |psi_i - y1i| * |x2I| + y0r * xR + y0i * xI} + + // xR = 1/sqrt(2) and xI = 1/sqrt(2) + // For numerator: bit_met_num_re_p = abs_psi_rpm + abs_psi_imm + y0r/sqrt(2) + y0i/sqrt(2) + simde__m128i bit_met_num_re_p = simde_mm_adds_epi16(abs_psi_rpm, abs_psi_imm); + bit_met_num_re_p = simde_mm_adds_epi16(bit_met_num_re_p, y0r_over2); + bit_met_num_re_p = simde_mm_adds_epi16(bit_met_num_re_p, y0i_over2); + + // xR = 1/sqrt(2) and xI = -1/sqrt(2) + // For numerator: bit_met_num_re_m = abs_psi_rmm + abs_psi_ipp + y0r/sqrt(2) - y0i/sqrt(2) + simde__m128i bit_met_num_re_m = simde_mm_adds_epi16(abs_psi_rmm, abs_psi_ipp); + bit_met_num_re_m = simde_mm_adds_epi16(bit_met_num_re_m, y0r_over2); + bit_met_num_re_m = simde_mm_subs_epi16(bit_met_num_re_m, y0i_over2); + + // xR = -1/sqrt(2) and xI = 1/sqrt(2) + // For denominator: bit_met_den_re_p = abs_psi_rmp + abs_psi_ipm - y0r/sqrt(2) + y0i/sqrt(2) + simde__m128i bit_met_den_re_p = simde_mm_adds_epi16(abs_psi_rmp, abs_psi_ipm); + bit_met_den_re_p = simde_mm_subs_epi16(bit_met_den_re_p, y0r_over2); + bit_met_den_re_p = simde_mm_adds_epi16(bit_met_den_re_p, y0i_over2); + + // xR = -1/sqrt(2) and xI = -1/sqrt(2) + // For denominator: bit_met_den_re_m = abs_psi_rpp + abs_psi_imp - y0r/sqrt(2) - y0i/sqrt(2) + simde__m128i bit_met_den_re_m = simde_mm_adds_epi16(abs_psi_rpp, abs_psi_imp); + bit_met_den_re_m = simde_mm_subs_epi16(bit_met_den_re_m, y0r_over2); + bit_met_den_re_m = simde_mm_subs_epi16(bit_met_den_re_m, y0i_over2); + + // xR = 1/sqrt(2) and xI = 1/sqrt(2) + // For numerator: bit_met_num_im_p = abs_psi_rpm + abs_psi_imm + y0r/sqrt(2) + y0i/sqrt(2) + simde__m128i bit_met_num_im_p = simde_mm_adds_epi16(abs_psi_rpm, abs_psi_imm); + bit_met_num_im_p = simde_mm_adds_epi16(bit_met_num_im_p, y0r_over2); + bit_met_num_im_p = simde_mm_adds_epi16(bit_met_num_im_p, y0i_over2); + + // xR = -1/sqrt(2) and xI = 1/sqrt(2) + // For numerator: bit_met_num_im_m = abs_psi_rmp + abs_psi_ipm - y0r/sqrt(2) + y0i/sqrt(2) + simde__m128i bit_met_num_im_m = simde_mm_adds_epi16(abs_psi_rmp, abs_psi_ipm); + bit_met_num_im_m = simde_mm_subs_epi16(bit_met_num_im_m, y0r_over2); + bit_met_num_im_m = simde_mm_adds_epi16(bit_met_num_im_m, y0i_over2); + + // xR = 1/sqrt(2) and xI = -1/sqrt(2) + // For denominator: bit_met_den_im_p = abs_psi_rmm + abs_psi_ipp + y0r/sqrt(2) - y0i/sqrt(2) + simde__m128i bit_met_den_im_p = simde_mm_adds_epi16(abs_psi_rmm, abs_psi_ipp); + bit_met_den_im_p = simde_mm_adds_epi16(bit_met_den_im_p, y0r_over2); + bit_met_den_im_p = simde_mm_subs_epi16(bit_met_den_im_p, y0i_over2); + + // xR = -1/sqrt(2) and xI = -1/sqrt(2) + // For denominator: bit_met_den_im_m = abs_psi_rpp + abs_psi_imp - y0r/sqrt(2)- y0i/sqrt(2) + simde__m128i bit_met_den_im_m = simde_mm_adds_epi16(abs_psi_rpp, abs_psi_imp); + bit_met_den_im_m = simde_mm_subs_epi16(bit_met_den_im_m, y0r_over2); + bit_met_den_im_m = simde_mm_subs_epi16(bit_met_den_im_m, y0i_over2); + + /// Compute the LLRs + + // LLR = lambda(c==1) - lambda(c==0) + + simde__m128i logmax_num_re0 = simde_mm_max_epi16(bit_met_num_re_p, bit_met_num_re_m); // LLR of the first bit: Bit = 1 + simde__m128i logmax_den_re0 = simde_mm_max_epi16(bit_met_den_re_p, bit_met_den_re_m); // LLR of the first bit: Bit = 0 + simde__m128i logmax_num_im0 = simde_mm_max_epi16(bit_met_num_im_p, bit_met_num_im_m); // LLR of the second bit: Bit = 1 + simde__m128i logmax_den_im0 = simde_mm_max_epi16(bit_met_den_im_p, bit_met_den_im_m); // LLR of the second bit: Bit = 0 + + y0r = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); // LLR of first bit [L1(1), L1(2), L1(3), L1(4)] + y0i = simde_mm_subs_epi16(logmax_num_im0, logmax_den_im0); // LLR of second bit [L2(1), L2(2), L2(3), L2(4)] + + // [L1(1), L2(1), L1(2), L2(2)] + simde_mm_storeu_si128(&stream0_128i_out[i], simde_mm_unpacklo_epi16(y0r, y0i)); + + // false if only 2 REs remain + if (i < ((length >> 1) - 1)) { + simde_mm_storeu_si128(&stream0_128i_out[i + 1], simde_mm_unpackhi_epi16(y0r, y0i)); + } + } +#else + + simde__m256i *rho01_256i = (simde__m256i *)rho01; + simde__m256i *stream0_256i_in = (simde__m256i *)stream0_in; + simde__m256i *stream1_256i_in = (simde__m256i *)stream1_in; + simde__m256i *stream0_256i_out = (simde__m256i *)stream0_out; + simde__m256i ONE_OVER_2_SQRT_2 = simde_mm256_set1_epi16(23170); // round(2 ^ 16 / (2 * sqrt(2))) + + // In each iteration, we take 16 complex symbols + for (int i = 0; i < length >> 3; i += 2) { + /// Compute real and imaginary parts of MF output for stream 0 (desired stream) + simde__m256i y0r, y0i; + oai_mm256_separate_real_imag_parts(&y0r, &y0i, stream0_256i_in[i], stream0_256i_in[i + 1]); + simde__m256i y0r_over2 = simde_mm256_mulhi_epi16(y0r, ONE_OVER_2_SQRT_2); + y0r_over2 = simde_mm256_slli_epi16(y0r_over2, 1); // y0r_over2 = Re(y0) / sqrt(2) + simde__m256i y0i_over2 = simde_mm256_mulhi_epi16(y0i, ONE_OVER_2_SQRT_2); + y0i_over2 = simde_mm256_slli_epi16(y0i_over2, 1); // y0i_over2 = Im(y0) / sqrt(2) + + /// Compute real and imaginary parts of MF output for stream 1 (interference stream) + simde__m256i y1r_over2, y1i_over2; + oai_mm256_separate_real_imag_parts(&y1r_over2, &y1i_over2, stream1_256i_in[i], stream1_256i_in[i + 1]); + y1r_over2 = simde_mm256_srai_epi16(y1r_over2, 1); // y1r_over2 = Re(y1) / 2 + y1i_over2 = simde_mm256_srai_epi16(y1i_over2, 1); // y1i_over2 = Im(y1) / 2 + + /// Get real and imaginary parts of rho + simde__m256i rhor, rhoi; + oai_mm256_separate_real_imag_parts(&rhor, &rhoi, rho01_256i[i], rho01_256i[i + 1]); + + /// Compute |psi_r| and |psi_i| + + // psi_r = rhor * xR + rhoi * xI + // psi_i = rhor * xI - rhoi * xR + + // Put (rho_r + rho_i)/(2*sqrt(2)) in rho_p + // rhor * xR + rhoi * xI --> xR = 1/sqrt(2) and xI = 1/sqrt(2) + // rhor * xI - rhoi * xR --> xR = -1/sqrt(2) and xI = 1/sqrt(2) + simde__m256i rho_p = simde_mm256_adds_epi16(rhor, rhoi); // rho_p = Re(rho) + Im(rho) + rho_p = simde_mm256_mulhi_epi16(rho_p, ONE_OVER_2_SQRT_2); // rho_p = rho_p / (2*sqrt(2)) + + // Put (rho_r - rho_i)/(2*sqrt(2)) in rho_m + // rhor * xR + rhoi * xI --> xR = 1/sqrt(2) and xI = -1/sqrt(2) + // rhor * xI - rhoi * xR --> xR = 1/sqrt(2) and xI = 1/sqrt(2) + simde__m256i rho_m = simde_mm256_subs_epi16(rhor, rhoi); // rho_m = Re(rho) - Im(rho) + rho_m = simde_mm256_mulhi_epi16(rho_m, ONE_OVER_2_SQRT_2); // rho_m = rho_m / (2*sqrt(2)) + + // xR = 1/sqrt(2) and xI = 1/sqrt(2) + simde__m256i abs_psi_rpm = simde_mm256_subs_epi16(rho_p, y1r_over2); // psi_rpm = rho_p - y1r/2 + abs_psi_rpm = simde_mm256_abs_epi16(abs_psi_rpm); // abs_psi_rpm = |psi_rpm| + + // xR = 1/sqrt(2) and xI = 1/sqrt(2) + simde__m256i abs_psi_imm = simde_mm256_subs_epi16(rho_m, y1i_over2); // psi_imm = rho_m - y1i/2 + abs_psi_imm = simde_mm256_abs_epi16(abs_psi_imm); // abs_psi_imm = |psi_imm| + + // xR = 1/sqrt(2) and xI = -1/sqrt(2) + simde__m256i abs_psi_rmm = simde_mm256_subs_epi16(rho_m, y1r_over2); // psi_rmm = rho_m - y1r/2 + abs_psi_rmm = simde_mm256_abs_epi16(abs_psi_rmm); // abs_psi_rmm = |psi_rmm| + + // xR = -1/sqrt(2) and xI = 1/sqrt(2) + simde__m256i abs_psi_ipm = simde_mm256_subs_epi16(rho_p, y1i_over2); // psi_ipm = rho_p - y1i/2 + abs_psi_ipm = simde_mm256_abs_epi16(abs_psi_ipm); // abs_psi_ipm = |psi_ipm| + + // xR = -1/sqrt(2) and xI = -1/sqrt(2) + simde__m256i abs_psi_rpp = simde_mm256_adds_epi16(rho_p, y1r_over2); // psi_rpp = rho_p + y1r/2 + abs_psi_rpp = simde_mm256_abs_epi16(abs_psi_rpp); // abs_psi_rpp = |psi_rpp| + + // xR = -1/sqrt(2) and xI = -1/sqrt(2) + simde__m256i abs_psi_imp = simde_mm256_adds_epi16(rho_m, y1i_over2); // psi_imp = rho_m + y1i/2 + abs_psi_imp = simde_mm256_abs_epi16(abs_psi_imp); // abs_psi_imp = |psi_imp| + + // xR = -1/sqrt(2) and xI = 1/sqrt(2) + simde__m256i abs_psi_rmp = simde_mm256_adds_epi16(rho_m, y1r_over2); // psi_rmp = rho_m + y1r/2 + abs_psi_rmp = simde_mm256_abs_epi16(abs_psi_rmp); // abs_psi_rmp = |psi_rmp| + + // xR = 1/sqrt(2) and xI = -1/sqrt(2) + simde__m256i abs_psi_ipp = simde_mm256_adds_epi16(rho_p, y1i_over2); // psi_ipm = rho_p + y1i/2 + abs_psi_ipp = simde_mm256_abs_epi16(abs_psi_ipp); // abs_psi_ipp = |psi_ipm| + + /// Compute bit metrics (lambda) + + // lambda = max { |psi_r - y1r| * |x2R| + |psi_i - y1i| * |x2I| + y0r * xR + y0i * xI} + + // xR = 1/sqrt(2) and xI = 1/sqrt(2) + // For numerator: bit_met_num_re_p = abs_psi_rpm + abs_psi_imm + y0r/sqrt(2) + y0i/sqrt(2) + simde__m256i bit_met_num_re_p = simde_mm256_adds_epi16(abs_psi_rpm, abs_psi_imm); + bit_met_num_re_p = simde_mm256_adds_epi16(bit_met_num_re_p, y0r_over2); + bit_met_num_re_p = simde_mm256_adds_epi16(bit_met_num_re_p, y0i_over2); + + // xR = 1/sqrt(2) and xI = -1/sqrt(2) + // For numerator: bit_met_num_re_m = abs_psi_rmm + abs_psi_ipp + y0r/sqrt(2) - y0i/sqrt(2) + simde__m256i bit_met_num_re_m = simde_mm256_adds_epi16(abs_psi_rmm, abs_psi_ipp); + bit_met_num_re_m = simde_mm256_adds_epi16(bit_met_num_re_m, y0r_over2); + bit_met_num_re_m = simde_mm256_subs_epi16(bit_met_num_re_m, y0i_over2); + + // xR = -1/sqrt(2) and xI = 1/sqrt(2) + // For denominator: bit_met_den_re_p = abs_psi_rmp + abs_psi_ipm - y0r/sqrt(2) + y0i/sqrt(2) + simde__m256i bit_met_den_re_p = simde_mm256_adds_epi16(abs_psi_rmp, abs_psi_ipm); + bit_met_den_re_p = simde_mm256_subs_epi16(bit_met_den_re_p, y0r_over2); + bit_met_den_re_p = simde_mm256_adds_epi16(bit_met_den_re_p, y0i_over2); + + // xR = -1/sqrt(2) and xI = -1/sqrt(2) + // For denominator: bit_met_den_re_m = abs_psi_rpp + abs_psi_imp - y0r/sqrt(2) - y0i/sqrt(2) + simde__m256i bit_met_den_re_m = simde_mm256_adds_epi16(abs_psi_rpp, abs_psi_imp); + bit_met_den_re_m = simde_mm256_subs_epi16(bit_met_den_re_m, y0r_over2); + bit_met_den_re_m = simde_mm256_subs_epi16(bit_met_den_re_m, y0i_over2); + + // xR = 1/sqrt(2) and xI = 1/sqrt(2) + // For numerator: bit_met_num_im_p = abs_psi_rpm + abs_psi_imm + y0r/sqrt(2) + y0i/sqrt(2) + simde__m256i bit_met_num_im_p = simde_mm256_adds_epi16(abs_psi_rpm, abs_psi_imm); + bit_met_num_im_p = simde_mm256_adds_epi16(bit_met_num_im_p, y0r_over2); + bit_met_num_im_p = simde_mm256_adds_epi16(bit_met_num_im_p, y0i_over2); + + // xR = -1/sqrt(2) and xI = 1/sqrt(2) + // For numerator: bit_met_num_im_m = abs_psi_rmp + abs_psi_ipm - y0r/sqrt(2) + y0i/sqrt(2) + simde__m256i bit_met_num_im_m = simde_mm256_adds_epi16(abs_psi_rmp, abs_psi_ipm); + bit_met_num_im_m = simde_mm256_subs_epi16(bit_met_num_im_m, y0r_over2); + bit_met_num_im_m = simde_mm256_adds_epi16(bit_met_num_im_m, y0i_over2); + + // xR = 1/sqrt(2) and xI = -1/sqrt(2) + // For denominator: bit_met_den_im_p = abs_psi_rmm + abs_psi_ipp + y0r/sqrt(2) - y0i/sqrt(2) + simde__m256i bit_met_den_im_p = simde_mm256_adds_epi16(abs_psi_rmm, abs_psi_ipp); + bit_met_den_im_p = simde_mm256_adds_epi16(bit_met_den_im_p, y0r_over2); + bit_met_den_im_p = simde_mm256_subs_epi16(bit_met_den_im_p, y0i_over2); + + // xR = -1/sqrt(2) and xI = -1/sqrt(2) + // For denominator: bit_met_den_im_m = abs_psi_rpp + abs_psi_imp - y0r/sqrt(2)- y0i/sqrt(2) + simde__m256i bit_met_den_im_m = simde_mm256_adds_epi16(abs_psi_rpp, abs_psi_imp); + bit_met_den_im_m = simde_mm256_subs_epi16(bit_met_den_im_m, y0r_over2); + bit_met_den_im_m = simde_mm256_subs_epi16(bit_met_den_im_m, y0i_over2); + + /// Compute the LLRs + + // LLR = lambda(c==1) - lambda(c==0) + + simde__m256i logmax_num_re0 = simde_mm256_max_epi16(bit_met_num_re_p, bit_met_num_re_m); // LLR of the first bit: Bit = 1 + simde__m256i logmax_den_re0 = simde_mm256_max_epi16(bit_met_den_re_p, bit_met_den_re_m); // LLR of the first bit: Bit = 0 + simde__m256i logmax_num_im0 = simde_mm256_max_epi16(bit_met_num_im_p, bit_met_num_im_m); // LLR of the second bit: Bit = 1 + simde__m256i logmax_den_im0 = simde_mm256_max_epi16(bit_met_den_im_p, bit_met_den_im_m); // LLR of the second bit: Bit = 0 + + y0r = simde_mm256_subs_epi16(logmax_num_re0, + logmax_den_re0); // LLR of first bit [L1(1), L1(2), L1(3), L1(4), L1(5), L1(6), L1(7), L1(8)] + y0i = simde_mm256_subs_epi16(logmax_num_im0, + logmax_den_im0); // LLR of second bit [L2(1), L2(2), L2(3), L2(4), L2(5), L2(6), L2(7), L2(8)] + + // [L1(1), L2(1), L1(2), L2(2) ...] + simde__m128i *stream0_128i_out = (simde__m128i *)&stream0_256i_out[i]; + simde__m128i *y0r_128 = (simde__m128i *)&y0r; + simde__m128i *y0i_128 = (simde__m128i *)&y0i; + simde_mm_storeu_si128(&stream0_128i_out[0], simde_mm_unpacklo_epi16(y0r_128[0], y0i_128[0])); + simde_mm_storeu_si128(&stream0_128i_out[1], simde_mm_unpackhi_epi16(y0r_128[0], y0i_128[0])); + + // false if only 4 REs remain + if (i < ((length >> 2) - 1)) { + simde__m128i *stream0_128i_out = (simde__m128i *)&stream0_256i_out[i + 1]; + simde_mm_storeu_si128(&stream0_128i_out[0], simde_mm_unpacklo_epi16(y0r_128[1], y0i_128[1])); + simde_mm_storeu_si128(&stream0_128i_out[1], simde_mm_unpackhi_epi16(y0r_128[1], y0i_128[1])); + } + } +#endif +} + +#ifdef USE_128BIT +// calculate interference magnitude +// tmp_result = ones in shorts corr. to interval 2<=x<=4, tmp_result2 interval < 2, tmp_result3 interval 46 +static inline simde__m128i interference_abs_64qam_epi16(simde__m128i psi, + simde__m128i int_ch_mag, + simde__m128i int_two_ch_mag, + simde__m128i int_three_ch_mag, + simde__m128i c1, + simde__m128i c3, + simde__m128i c5, + simde__m128i c7) +{ + simde__m128i tmp_result = simde_mm_cmpgt_epi16(int_two_ch_mag, psi); + simde__m128i tmp_result3 = simde_mm_xor_si128(tmp_result, allones128()); + simde__m128i tmp_result2 = simde_mm_cmpgt_epi16(int_ch_mag, psi); + tmp_result = simde_mm_xor_si128(tmp_result, tmp_result2); + simde__m128i tmp_result4 = simde_mm_cmpgt_epi16(psi, int_three_ch_mag); + tmp_result3 = simde_mm_xor_si128(tmp_result3, tmp_result4); + tmp_result = simde_mm_and_si128(tmp_result, c3); + tmp_result2 = simde_mm_and_si128(tmp_result2, c1); + tmp_result3 = simde_mm_and_si128(tmp_result3, c5); + tmp_result4 = simde_mm_and_si128(tmp_result4, c7); + tmp_result = simde_mm_or_si128(tmp_result, tmp_result2); + tmp_result3 = simde_mm_or_si128(tmp_result3, tmp_result4); + return simde_mm_or_si128(tmp_result, tmp_result3); +} + +// Calculates psi_a = psi_r * a_r + psi_i * a_i +static inline simde__m128i prodsum_psi_a_epi16(simde__m128i psi_r, simde__m128i a_r, simde__m128i psi_i, simde__m128i a_i) +{ + simde__m128i tmp_result = simde_mm_mulhi_epi16(psi_r, a_r); + tmp_result = simde_mm_slli_epi16(tmp_result, 1); + simde__m128i tmp_result2 = simde_mm_mulhi_epi16(psi_i, a_i); + tmp_result2 = simde_mm_slli_epi16(tmp_result2, 1); + return simde_mm_adds_epi16(tmp_result, tmp_result2); +} + +// Calculate interference magnitude +static inline simde__m128i interference_abs_epi16(simde__m128i psi, simde__m128i int_ch_mag, simde__m128i c1, simde__m128i c2) +{ + simde__m128i tmp_result = simde_mm_cmplt_epi16(psi, int_ch_mag); + simde__m128i tmp_result2 = simde_mm_xor_si128(tmp_result, allones128()); + tmp_result = simde_mm_and_si128(tmp_result, c1); + tmp_result2 = simde_mm_and_si128(tmp_result2, c2); + return simde_mm_or_si128(tmp_result, tmp_result2); +} + +// Calculates a_sq = int_ch_mag * (a_r^2 + a_i^2) * scale_factor +static inline simde__m128i square_a_epi16(simde__m128i a_r, simde__m128i a_i, simde__m128i int_ch_mag, simde__m128i scale_factor) +{ + simde__m128i tmp_result = simde_mm_mulhi_epi16(a_r, a_r); + tmp_result = simde_mm_slli_epi16(tmp_result, 1); + tmp_result = simde_mm_mulhi_epi16(tmp_result, scale_factor); + tmp_result = simde_mm_slli_epi16(tmp_result, 1); + tmp_result = simde_mm_mulhi_epi16(tmp_result, int_ch_mag); + tmp_result = simde_mm_slli_epi16(tmp_result, 1); + simde__m128i tmp_result2 = simde_mm_mulhi_epi16(a_i, a_i); + tmp_result2 = simde_mm_slli_epi16(tmp_result2, 1); + tmp_result2 = simde_mm_mulhi_epi16(tmp_result2, scale_factor); + tmp_result2 = simde_mm_slli_epi16(tmp_result2, 1); + tmp_result2 = simde_mm_mulhi_epi16(tmp_result2, int_ch_mag); + tmp_result2 = simde_mm_slli_epi16(tmp_result2, 1); + return simde_mm_adds_epi16(tmp_result, tmp_result2); +} + +// calculates a_sq = int_ch_mag*(a_r^2 + a_i^2)*scale_factor for 64-QAM +static inline simde__m128i square_a_64qam_epi16(simde__m128i a_r, + simde__m128i a_i, + simde__m128i int_ch_mag, + simde__m128i scale_factor) +{ + simde__m128i tmp_result = simde_mm_mulhi_epi16(a_r, a_r); + tmp_result = simde_mm_slli_epi16(tmp_result, 1); + tmp_result = simde_mm_mulhi_epi16(tmp_result, scale_factor); + tmp_result = simde_mm_slli_epi16(tmp_result, 3); + tmp_result = simde_mm_mulhi_epi16(tmp_result, int_ch_mag); + tmp_result = simde_mm_slli_epi16(tmp_result, 1); + simde__m128i tmp_result2 = simde_mm_mulhi_epi16(a_i, a_i); + tmp_result2 = simde_mm_slli_epi16(tmp_result2, 1); + tmp_result2 = simde_mm_mulhi_epi16(tmp_result2, scale_factor); + tmp_result2 = simde_mm_slli_epi16(tmp_result2, 3); + tmp_result2 = simde_mm_mulhi_epi16(tmp_result2, int_ch_mag); + tmp_result2 = simde_mm_slli_epi16(tmp_result2, 1); + return simde_mm_adds_epi16(tmp_result, tmp_result2); +} + +static inline simde__m128i max_epi16(simde__m128i m0, + simde__m128i m1, + simde__m128i m2, + simde__m128i m3, + simde__m128i m4, + simde__m128i m5, + simde__m128i m6, + simde__m128i m7) +{ + simde__m128i a0 = simde_mm_max_epi16(m0, m1); + simde__m128i a1 = simde_mm_max_epi16(m2, m3); + simde__m128i a2 = simde_mm_max_epi16(m4, m5); + simde__m128i a3 = simde_mm_max_epi16(m6, m7); + simde__m128i b0 = simde_mm_max_epi16(a0, a1); + simde__m128i b1 = simde_mm_max_epi16(a2, a3); + return simde_mm_max_epi16(b0, b1); +} + +#else + +// calculate interference magnitude +// tmp_result = ones in shorts corr. to interval 2<=x<=4, tmp_result2 interval < 2, tmp_result3 interval 46 +static inline simde__m256i interference_abs_64qam_epi16_256(simde__m256i psi, + simde__m256i int_ch_mag, + simde__m256i int_two_ch_mag, + simde__m256i int_three_ch_mag, + simde__m256i c1, + simde__m256i c3, + simde__m256i c5, + simde__m256i c7) +{ + simde__m256i tmp_result = simde_mm256_cmpgt_epi16(int_two_ch_mag, psi); + simde__m256i tmp_result3 = simde_mm256_xor_si256(tmp_result, allones256()); + simde__m256i tmp_result2 = simde_mm256_cmpgt_epi16(int_ch_mag, psi); + tmp_result = simde_mm256_xor_si256(tmp_result, tmp_result2); + simde__m256i tmp_result4 = simde_mm256_cmpgt_epi16(psi, int_three_ch_mag); + tmp_result3 = simde_mm256_xor_si256(tmp_result3, tmp_result4); + tmp_result = simde_mm256_and_si256(tmp_result, c3); + tmp_result2 = simde_mm256_and_si256(tmp_result2, c1); + tmp_result3 = simde_mm256_and_si256(tmp_result3, c5); + tmp_result4 = simde_mm256_and_si256(tmp_result4, c7); + tmp_result = simde_mm256_or_si256(tmp_result, tmp_result2); + tmp_result3 = simde_mm256_or_si256(tmp_result3, tmp_result4); + return simde_mm256_or_si256(tmp_result, tmp_result3); +} + +// calculates psi_a = psi_r*a_r + psi_i*a_i +static inline simde__m256i prodsum_psi_a_epi16_256(simde__m256i psi_r, simde__m256i a_r, simde__m256i psi_i, simde__m256i a_i) +{ + simde__m256i tmp_result = simde_mm256_mulhi_epi16(psi_r, a_r); + tmp_result = simde_mm256_slli_epi16(tmp_result, 1); + simde__m256i tmp_result2 = simde_mm256_mulhi_epi16(psi_i, a_i); + tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 1); + return simde_mm256_adds_epi16(tmp_result, tmp_result2); +} + +// Calculate interference magnitude +static inline simde__m256i interference_abs_epi16_256(simde__m256i psi, simde__m256i int_ch_mag, simde__m256i c1, simde__m256i c2) +{ + simde__m256i tmp_result = simde_mm256_cmpgt_epi16(int_ch_mag, psi); + simde__m256i tmp_result2 = simde_mm256_xor_si256(tmp_result, allones256()); + tmp_result = simde_mm256_and_si256(tmp_result, c1); + tmp_result2 = simde_mm256_and_si256(tmp_result2, c2); + return simde_mm256_or_si256(tmp_result, tmp_result2); +} + +// Calculates a_sq = int_ch_mag * (a_r^2 + a_i^2) * scale_factor +static inline simde__m256i square_a_epi16_256(simde__m256i a_r, + simde__m256i a_i, + simde__m256i int_ch_mag, + simde__m256i scale_factor) +{ + simde__m256i tmp_result = simde_mm256_mulhi_epi16(a_r, a_r); + tmp_result = simde_mm256_slli_epi16(tmp_result, 1); + tmp_result = simde_mm256_mulhi_epi16(tmp_result, scale_factor); + tmp_result = simde_mm256_slli_epi16(tmp_result, 1); + tmp_result = simde_mm256_mulhi_epi16(tmp_result, int_ch_mag); + tmp_result = simde_mm256_slli_epi16(tmp_result, 1); + simde__m256i tmp_result2 = simde_mm256_mulhi_epi16(a_i, a_i); + tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 1); + tmp_result2 = simde_mm256_mulhi_epi16(tmp_result2, scale_factor); + tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 1); + tmp_result2 = simde_mm256_mulhi_epi16(tmp_result2, int_ch_mag); + tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 1); + return simde_mm256_adds_epi16(tmp_result, tmp_result2); +} + +// calculates a_sq = int_ch_mag*(a_r^2 + a_i^2)*scale_factor for 64-QAM +static inline simde__m256i square_a_64qam_epi16_256(simde__m256i a_r, + simde__m256i a_i, + simde__m256i int_ch_mag, + simde__m256i scale_factor) +{ + simde__m256i tmp_result = simde_mm256_mulhi_epi16(a_r, a_r); + tmp_result = simde_mm256_slli_epi16(tmp_result, 1); + tmp_result = simde_mm256_mulhi_epi16(tmp_result, scale_factor); + tmp_result = simde_mm256_slli_epi16(tmp_result, 3); + tmp_result = simde_mm256_mulhi_epi16(tmp_result, int_ch_mag); + tmp_result = simde_mm256_slli_epi16(tmp_result, 1); + simde__m256i tmp_result2 = simde_mm256_mulhi_epi16(a_i, a_i); + tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 1); + tmp_result2 = simde_mm256_mulhi_epi16(tmp_result2, scale_factor); + tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 3); + tmp_result2 = simde_mm256_mulhi_epi16(tmp_result2, int_ch_mag); + tmp_result2 = simde_mm256_slli_epi16(tmp_result2, 1); + return simde_mm256_adds_epi16(tmp_result, tmp_result2); +} + +static inline simde__m256i max_epi16_256(simde__m256i m0, + simde__m256i m1, + simde__m256i m2, + simde__m256i m3, + simde__m256i m4, + simde__m256i m5, + simde__m256i m6, + simde__m256i m7) +{ + simde__m256i a0 = simde_mm256_max_epi16(m0, m1); + simde__m256i a1 = simde_mm256_max_epi16(m2, m3); + simde__m256i a2 = simde_mm256_max_epi16(m4, m5); + simde__m256i a3 = simde_mm256_max_epi16(m6, m7); + simde__m256i b0 = simde_mm256_max_epi16(a0, a1); + simde__m256i b1 = simde_mm256_max_epi16(a2, a3); + return simde_mm256_max_epi16(b0, b1); +} + +#endif + +/* + * This function computes the LLRs of stream 0 (s_0) in presence of the interfering stream 1 (s_1) assuming that both symbols are + * 16QAM. It can be used for both MU-MIMO interference-aware receiver or for SU-MIMO receivers. + * + * Input: + * stream0_in: MF filter output for 1st stream, i.e., y0' = h0'*y0 + * stream1_in: MF filter output for 2nd stream, i.e., y1' = h1'*y0 + * ch_mag: 2*h0/sqrt(10), [Re0 Im0 Re1 Im1] s.t. Im0=Re0, Im1=Re1, etc + * ch_mag_i: 2*h1/sqrt(10), [Re0 Im0 Re1 Im1] s.t. Im0=Re0, Im1=Re1, etc + * rho01: Channel cross correlation, i.e., rho01 = h0'*h1 + * length: Number of resource elements + * + * Output: + * stream0_out: Output LLRs for 1st stream + */ +void nr_qam16_llr_2layer(c16_t *stream0_in, + c16_t *stream1_in, + c16_t *ch_mag, + c16_t *ch_mag_i, + int16_t *stream0_out, + c16_t *rho01, + uint32_t length) +{ +#ifdef USE_128BIT + simde__m128i *rho01_128i = (simde__m128i *)rho01; + simde__m128i *stream0_128i_in = (simde__m128i *)stream0_in; + simde__m128i *stream1_128i_in = (simde__m128i *)stream1_in; + simde__m128i *stream0_128i_out = (simde__m128i *)stream0_out; + simde__m128i *ch_mag_128i = (simde__m128i *)ch_mag; + simde__m128i *ch_mag_128i_i = (simde__m128i *)ch_mag_i; + + simde__m128i ONE_OVER_SQRT_10 = simde_mm_set1_epi16(20724); // round(1/sqrt(10)*2^16) + simde__m128i ONE_OVER_SQRT_10_Q15 = simde_mm_set1_epi16(10362); // round(1/sqrt(10)*2^15) + simde__m128i THREE_OVER_SQRT_10 = simde_mm_set1_epi16(31086); // round(3/sqrt(10)*2^15) + simde__m128i SQRT_10_OVER_FOUR = simde_mm_set1_epi16(25905); // round(sqrt(10)/4*2^15) + simde__m128i ONE_OVER_TWO_SQRT_10 = simde_mm_set1_epi16(10362); // round(1/2/sqrt(10)*2^16) + simde__m128i NINE_OVER_TWO_SQRT_10 = simde_mm_set1_epi16(23315); // round(9/2/sqrt(10)*2^14) + simde__m128i ch_mag_des, ch_mag_int; + simde__m128i y0r_over_sqrt10; + simde__m128i y0i_over_sqrt10; + simde__m128i y0r_three_over_sqrt10; + simde__m128i y0i_three_over_sqrt10; + simde__m128i ch_mag_over_10; + simde__m128i ch_mag_over_2; + simde__m128i ch_mag_9_over_10; + + simde__m128i xmm0; + simde__m128i xmm1; + simde__m128i xmm2; + simde__m128i xmm3; + simde__m128i xmm4; + simde__m128i xmm5; + simde__m128i xmm6; + simde__m128i xmm7; + + simde__m128i rho_rpi; + simde__m128i rho_rmi; + simde__m128i rho_rs[8]; + simde__m128i psi_rs[16]; + simde__m128i psi_is[16]; + simde__m128i a_rs[16]; + simde__m128i a_is[16]; + simde__m128i psi_as[16]; + simde__m128i a_sqs[16]; + simde__m128i y0_s[8]; + + simde__m128i y0r; + simde__m128i y0i; + simde__m128i y1r; + simde__m128i y1i; + + // In one iteration, we deal with 8 REs + for (int i = 0; i < length >> 2; i += 2) { + // Get rho + oai_mm_separate_real_imag_parts(&xmm2, &xmm3, rho01_128i[i], rho01_128i[i + 1]); + rho_rpi = simde_mm_adds_epi16(xmm2, xmm3); // rho = Re(rho) + Im(rho) + rho_rmi = simde_mm_subs_epi16(xmm2, xmm3); // rho* = Re(rho) - Im(rho) + + // Compute the different rhos + rho_rs[0] = simde_mm_mulhi_epi16(rho_rpi, ONE_OVER_SQRT_10); + rho_rs[4] = simde_mm_mulhi_epi16(rho_rmi, ONE_OVER_SQRT_10); + rho_rs[3] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(rho_rpi, THREE_OVER_SQRT_10), 1); + rho_rs[7] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(rho_rmi, THREE_OVER_SQRT_10), 1); + + xmm4 = simde_mm_mulhi_epi16(xmm2, ONE_OVER_SQRT_10); // Re(rho) + xmm5 = simde_mm_mulhi_epi16(xmm3, THREE_OVER_SQRT_10); // Im(rho) + xmm5 = simde_mm_slli_epi16(xmm5, 1); + + rho_rs[1] = simde_mm_adds_epi16(xmm4, xmm5); + rho_rs[5] = simde_mm_subs_epi16(xmm4, xmm5); + + xmm6 = simde_mm_mulhi_epi16(xmm2, THREE_OVER_SQRT_10); // Re(rho) + xmm7 = simde_mm_mulhi_epi16(xmm3, ONE_OVER_SQRT_10); // Im(rho) + xmm6 = simde_mm_slli_epi16(xmm6, 1); + + rho_rs[2] = simde_mm_adds_epi16(xmm6, xmm7); + rho_rs[6] = simde_mm_subs_epi16(xmm6, xmm7); + + // Rearrange interfering MF output + oai_mm_separate_real_imag_parts(&y1r, &y1i, stream1_128i_in[i], stream1_128i_in[i + 1]); + + // | [Re(rho)+ Im(rho)]/sqrt(10) - y1r | + for (int j = 0; j < 8; j++) { // psi_rs[0~7], rho_rs[0~7] + psi_rs[j] = simde_mm_abs_epi16(simde_mm_subs_epi16(rho_rs[j], y1r)); + } + for (int j = 8; j < 16; j++) { // psi_rs[8~16], rho_rs[4,5,6,7,0,1,2,3] + psi_rs[j] = simde_mm_abs_epi16(simde_mm_adds_epi16(rho_rs[(j - 4) & 7], y1r)); + } + const uint8_t rho_rs_indexes[16] = {4, 6, 5, 7, 0, 2, 1, 3, 0, 2, 1, 3, 4, 6, 5, 7}; + for (int k = 0; k < 16; + k += 8) { // psi_is[0~15], sub(rho_rs[4,6,5,7]), add(rho_rs[0,2,1,3]), sub(rho_rs[0,2,1,3]), add(rho_rs[4,6,5,7]) + for (int j = k; j < k + 4; j++) { + psi_is[j] = simde_mm_abs_epi16(simde_mm_subs_epi16(rho_rs[rho_rs_indexes[j]], y1i)); + psi_is[j + 4] = simde_mm_abs_epi16(simde_mm_adds_epi16(rho_rs[rho_rs_indexes[j + 4]], y1i)); + } + } + + // Rearrange desired MF output + oai_mm_separate_real_imag_parts(&y0r, &y0i, stream0_128i_in[i], stream0_128i_in[i + 1]); + + // Rearrange desired channel magnitudes + // [|h|^2(1),|h|^2(2),|h|^2(3),|h|^2(4)]*(2/sqrt(10)) + oai_mm_separate_real_imag_parts(&ch_mag_des, &xmm2, ch_mag_128i[i], ch_mag_128i[i + 1]); + + // Rearrange interfering channel magnitudes + oai_mm_separate_real_imag_parts(&ch_mag_int, &xmm2, ch_mag_128i_i[i], ch_mag_128i_i[i + 1]); + + // Scale MF output of desired signal + y0r_over_sqrt10 = simde_mm_mulhi_epi16(y0r, ONE_OVER_SQRT_10); + y0i_over_sqrt10 = simde_mm_mulhi_epi16(y0i, ONE_OVER_SQRT_10); + y0r_three_over_sqrt10 = simde_mm_slli_epi16(simde_mm_mulhi_epi16(y0r, THREE_OVER_SQRT_10), 1); + y0i_three_over_sqrt10 = simde_mm_slli_epi16(simde_mm_mulhi_epi16(y0i, THREE_OVER_SQRT_10), 1); + + // Compute necessary combination of required terms + y0_s[0] = simde_mm_adds_epi16(y0r_over_sqrt10, y0i_over_sqrt10); + y0_s[4] = simde_mm_subs_epi16(y0r_over_sqrt10, y0i_over_sqrt10); + + y0_s[1] = simde_mm_adds_epi16(y0r_over_sqrt10, y0i_three_over_sqrt10); + y0_s[5] = simde_mm_subs_epi16(y0r_over_sqrt10, y0i_three_over_sqrt10); + + y0_s[2] = simde_mm_adds_epi16(y0r_three_over_sqrt10, y0i_over_sqrt10); + y0_s[6] = simde_mm_subs_epi16(y0r_three_over_sqrt10, y0i_over_sqrt10); + + y0_s[3] = simde_mm_adds_epi16(y0r_three_over_sqrt10, y0i_three_over_sqrt10); + y0_s[7] = simde_mm_subs_epi16(y0r_three_over_sqrt10, y0i_three_over_sqrt10); + + for (int j = 0; j < 16; j++) { + // Compute optimal interfering symbol magnitude + a_rs[j] = interference_abs_epi16(psi_rs[j], ch_mag_int, ONE_OVER_SQRT_10_Q15, THREE_OVER_SQRT_10); + a_is[j] = interference_abs_epi16(psi_is[j], ch_mag_int, ONE_OVER_SQRT_10_Q15, THREE_OVER_SQRT_10); + + // Calculation of groups of two terms in the bit metric involving product of psi and interference magnitude + psi_as[j] = prodsum_psi_a_epi16(psi_rs[j], a_rs[j], psi_is[j], a_is[j]); + + // squared interference magnitude times int. ch. power + a_sqs[j] = square_a_epi16(a_rs[j], a_is[j], ch_mag_int, SQRT_10_OVER_FOUR); + } + + // Computing different multiples of channel norms + ch_mag_over_10 = simde_mm_mulhi_epi16(ch_mag_des, ONE_OVER_TWO_SQRT_10); + ch_mag_over_2 = simde_mm_mulhi_epi16(ch_mag_des, SQRT_10_OVER_FOUR); + ch_mag_over_2 = simde_mm_slli_epi16(ch_mag_over_2, 1); + ch_mag_9_over_10 = simde_mm_mulhi_epi16(ch_mag_des, NINE_OVER_TWO_SQRT_10); + ch_mag_9_over_10 = simde_mm_slli_epi16(ch_mag_9_over_10, 2); + + /// Compute bit metrics (lambda) + simde__m128i bit_mets[16]; + for (int j = 0; j < 8; j += 4) { + bit_mets[j + 0] = simde_mm_subs_epi16(psi_as[j + 0], a_sqs[j + 0]); + bit_mets[j + 0] = simde_mm_adds_epi16(bit_mets[j + 0], y0_s[j + 0]); + bit_mets[j + 0] = simde_mm_subs_epi16(bit_mets[j + 0], ch_mag_over_10); + + bit_mets[j + 1] = simde_mm_subs_epi16(psi_as[j + 1], a_sqs[j + 1]); + bit_mets[j + 1] = simde_mm_adds_epi16(bit_mets[j + 1], y0_s[j + 1]); + bit_mets[j + 1] = simde_mm_subs_epi16(bit_mets[j + 1], ch_mag_over_2); + bit_mets[j + 2] = simde_mm_subs_epi16(psi_as[j + 2], a_sqs[j + 2]); + bit_mets[j + 2] = simde_mm_adds_epi16(bit_mets[j + 2], y0_s[j + 2]); + bit_mets[j + 2] = simde_mm_subs_epi16(bit_mets[j + 2], ch_mag_over_2); + + bit_mets[j + 3] = simde_mm_subs_epi16(psi_as[j + 3], a_sqs[j + 3]); + bit_mets[j + 3] = simde_mm_adds_epi16(bit_mets[j + 3], y0_s[j + 3]); + bit_mets[j + 3] = simde_mm_subs_epi16(bit_mets[j + 3], ch_mag_9_over_10); + } + for (int j = 8; j < 16; j += 4) { + bit_mets[j + 0] = simde_mm_subs_epi16(psi_as[j + 0], a_sqs[j + 0]); + bit_mets[j + 0] = simde_mm_subs_epi16(bit_mets[j + 0], y0_s[(j - 4) & 0x07]); + bit_mets[j + 0] = simde_mm_subs_epi16(bit_mets[j + 0], ch_mag_over_10); + + bit_mets[j + 1] = simde_mm_subs_epi16(psi_as[j + 1], a_sqs[j + 1]); + bit_mets[j + 1] = simde_mm_subs_epi16(bit_mets[j + 1], y0_s[(j - 3) & 0x07]); + bit_mets[j + 1] = simde_mm_subs_epi16(bit_mets[j + 1], ch_mag_over_2); + bit_mets[j + 2] = simde_mm_subs_epi16(psi_as[j + 2], a_sqs[j + 2]); + bit_mets[j + 2] = simde_mm_subs_epi16(bit_mets[j + 2], y0_s[(j - 2) & 0x07]); + bit_mets[j + 2] = simde_mm_subs_epi16(bit_mets[j + 2], ch_mag_over_2); + + bit_mets[j + 3] = simde_mm_subs_epi16(psi_as[j + 3], a_sqs[j + 3]); + bit_mets[j + 3] = simde_mm_subs_epi16(bit_mets[j + 3], y0_s[(j - 1) & 0x07]); + bit_mets[j + 3] = simde_mm_subs_epi16(bit_mets[j + 3], ch_mag_9_over_10); + } + /// Compute the LLRs + + // LLR = lambda(c==1) - lambda(c==0) + + // LLR of the first bit: Bit = 1 + simde__m128i logmax_num_re0 = + max_epi16(bit_mets[8], bit_mets[9], bit_mets[10], bit_mets[11], bit_mets[12], bit_mets[13], bit_mets[14], bit_mets[15]); + // LLR of the first bit: Bit = 0 + simde__m128i logmax_den_re0 = + max_epi16(bit_mets[0], bit_mets[1], bit_mets[2], bit_mets[3], bit_mets[4], bit_mets[5], bit_mets[6], bit_mets[7]); + // LLR of the second bit: Bit = 1 + simde__m128i logmax_num_re1 = + max_epi16(bit_mets[4], bit_mets[5], bit_mets[6], bit_mets[7], bit_mets[12], bit_mets[13], bit_mets[14], bit_mets[15]); + // LLR of the second bit: Bit = 0 + simde__m128i logmax_den_re1 = + max_epi16(bit_mets[0], bit_mets[1], bit_mets[3], bit_mets[2], bit_mets[8], bit_mets[9], bit_mets[10], bit_mets[11]); + // LLR of the third bit: Bit = 1 + simde__m128i logmax_num_im0 = + max_epi16(bit_mets[2], bit_mets[3], bit_mets[6], bit_mets[7], bit_mets[10], bit_mets[11], bit_mets[14], bit_mets[15]); + // LLR of the third bit: Bit = 0 + simde__m128i logmax_den_im0 = + max_epi16(bit_mets[0], bit_mets[1], bit_mets[4], bit_mets[5], bit_mets[8], bit_mets[9], bit_mets[12], bit_mets[13]); + // LLR of the fourth bit: Bit = 1 + simde__m128i logmax_num_im1 = + max_epi16(bit_mets[1], bit_mets[3], bit_mets[5], bit_mets[7], bit_mets[9], bit_mets[11], bit_mets[13], bit_mets[15]); + // LLR of the fourth bit: Bit = 0 + simde__m128i logmax_den_im1 = + max_epi16(bit_mets[0], bit_mets[2], bit_mets[4], bit_mets[6], bit_mets[8], bit_mets[10], bit_mets[12], bit_mets[14]); + + y0r = simde_mm_subs_epi16(logmax_den_re0, + logmax_num_re0); // LLR of first bit [L1(1), L1(2), L1(3), L1(4), L1(5), L1(6), L1(7), L1(8)] + y1r = simde_mm_subs_epi16(logmax_den_re1, + logmax_num_re1); // LLR of second bit [L2(1), L2(2), L2(3), L2(4), L2(5), L2(6), L2(7), L2(8)] + y0i = simde_mm_subs_epi16(logmax_den_im0, + logmax_num_im0); // LLR of third bit [L3(1), L3(2), L3(3), L3(4), L3(5), L3(6), L3(7), L3(8)] + y1i = simde_mm_subs_epi16(logmax_den_im1, + logmax_num_im1); // LLR of fourth bit [L4(1), L4(2), L4(3), L4(4), L4(5), L4(6), L4(7), L4(8)] + + // Pack LLRs in output + xmm0 = simde_mm_unpacklo_epi16(y0r, y1r); // [L1(1), L2(1), L1(2), L2(2), L1(3), L2(3), L1(4), L2(4)] + xmm1 = simde_mm_unpackhi_epi16(y0r, y1r); // [L1(5), L2(5), L1(6), L2(6), L1(7), L2(7), L1(8), L2(8)] + xmm2 = simde_mm_unpacklo_epi16(y0i, y1i); // [L3(1), L4(1), L3(2), L4(2), L3(3), L4(3), L3(4), L4(4)] + xmm3 = simde_mm_unpackhi_epi16(y0i, y1i); // [L3(5), L4(5), L3(6), L4(6), L3(7), L4(7), L3(8), L4(8)] + stream0_128i_out[2 * i + 0] = simde_mm_unpacklo_epi32(xmm0, xmm2); // 8 LLRs, 2 REs + stream0_128i_out[2 * i + 1] = simde_mm_unpackhi_epi32(xmm0, xmm2); // 8 LLRs, 2 REs + stream0_128i_out[2 * i + 2] = simde_mm_unpacklo_epi32(xmm1, xmm3); // 8 LLRs, 2 REs + stream0_128i_out[2 * i + 3] = simde_mm_unpackhi_epi32(xmm1, xmm3); // 8 LLRs, 2 REs + } +#else + simde__m256i *rho01_256i = (simde__m256i *)rho01; + simde__m256i *stream0_256i_in = (simde__m256i *)stream0_in; + simde__m256i *stream1_256i_in = (simde__m256i *)stream1_in; + simde__m256i *stream0_256i_out = (simde__m256i *)stream0_out; + simde__m256i *ch_mag_256i = (simde__m256i *)ch_mag; + simde__m256i *ch_mag_256i_i = (simde__m256i *)ch_mag_i; + + simde__m256i ONE_OVER_SQRT_10 = simde_mm256_set1_epi16(20724); // round(1/sqrt(10)*2^16) + simde__m256i ONE_OVER_SQRT_10_Q15 = simde_mm256_set1_epi16(10362); // round(1/sqrt(10)*2^15) + simde__m256i THREE_OVER_SQRT_10 = simde_mm256_set1_epi16(31086); // round(3/sqrt(10)*2^15) + simde__m256i SQRT_10_OVER_FOUR = simde_mm256_set1_epi16(25905); // round(sqrt(10)/4*2^15) + simde__m256i ONE_OVER_TWO_SQRT_10 = simde_mm256_set1_epi16(10362); // round(1/2/sqrt(10)*2^16) + simde__m256i NINE_OVER_TWO_SQRT_10 = simde_mm256_set1_epi16(23315); // round(9/2/sqrt(10)*2^14) + simde__m256i ch_mag_des, ch_mag_int; + simde__m256i y0r_over_sqrt10; + simde__m256i y0i_over_sqrt10; + simde__m256i y0r_three_over_sqrt10; + simde__m256i y0i_three_over_sqrt10; + simde__m256i ch_mag_over_10; + simde__m256i ch_mag_over_2; + simde__m256i ch_mag_9_over_10; + + simde__m256i xmm2; + simde__m256i xmm3; + simde__m256i xmm4; + simde__m256i xmm5; + simde__m256i xmm6; + simde__m256i xmm7; + + simde__m256i rho_rpi; + simde__m256i rho_rmi; + simde__m256i rho_rs[8]; + simde__m256i psi_rs[16]; + simde__m256i psi_is[16]; + simde__m256i a_rs[16]; + simde__m256i a_is[16]; + simde__m256i psi_as[16]; + simde__m256i a_sqs[16]; + simde__m256i y0_s[8]; + + simde__m256i y0r; + simde__m256i y0i; + simde__m256i y1r; + simde__m256i y1i; + + // In one iteration, we deal with 8 REs + for (int i = 0; i < length >> 3; i += 2) { + // Get rho + oai_mm256_separate_real_imag_parts(&xmm2, &xmm3, rho01_256i[i], rho01_256i[i + 1]); + rho_rpi = simde_mm256_adds_epi16(xmm2, xmm3); // rho = Re(rho) + Im(rho) + rho_rmi = simde_mm256_subs_epi16(xmm2, xmm3); // rho* = Re(rho) - Im(rho) + + // Compute the different rhos + rho_rs[0] = simde_mm256_mulhi_epi16(rho_rpi, ONE_OVER_SQRT_10); + rho_rs[4] = simde_mm256_mulhi_epi16(rho_rmi, ONE_OVER_SQRT_10); + rho_rs[3] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(rho_rpi, THREE_OVER_SQRT_10), 1); + rho_rs[7] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(rho_rmi, THREE_OVER_SQRT_10), 1); + + xmm4 = simde_mm256_mulhi_epi16(xmm2, ONE_OVER_SQRT_10); // Re(rho) + xmm5 = simde_mm256_mulhi_epi16(xmm3, THREE_OVER_SQRT_10); // Im(rho) + xmm5 = simde_mm256_slli_epi16(xmm5, 1); + + rho_rs[1] = simde_mm256_adds_epi16(xmm4, xmm5); + rho_rs[5] = simde_mm256_subs_epi16(xmm4, xmm5); + + xmm6 = simde_mm256_mulhi_epi16(xmm2, THREE_OVER_SQRT_10); // Re(rho) + xmm7 = simde_mm256_mulhi_epi16(xmm3, ONE_OVER_SQRT_10); // Im(rho) + xmm6 = simde_mm256_slli_epi16(xmm6, 1); + + rho_rs[2] = simde_mm256_adds_epi16(xmm6, xmm7); + rho_rs[6] = simde_mm256_subs_epi16(xmm6, xmm7); + + // Rearrange interfering MF output + oai_mm256_separate_real_imag_parts(&y1r, &y1i, stream1_256i_in[i], stream1_256i_in[i + 1]); + + // | [Re(rho)+ Im(rho)]/sqrt(10) - y1r | + for (int j = 0; j < 8; j++) { // psi_rs[0~7], rho_rs[0~7] + psi_rs[j] = simde_mm256_abs_epi16(simde_mm256_subs_epi16(rho_rs[j], y1r)); + } + for (int j = 8; j < 16; j++) { // psi_rs[8~16], rho_rs[4,5,6,7,0,1,2,3] + psi_rs[j] = simde_mm256_abs_epi16(simde_mm256_adds_epi16(rho_rs[(j - 4) & 7], y1r)); + } + const uint8_t rho_rs_indexes[16] = {4, 6, 5, 7, 0, 2, 1, 3, 0, 2, 1, 3, 4, 6, 5, 7}; + for (int k = 0; k < 16; + k += 8) { // psi_is[0~15], sub(rho_rs[4,6,5,7]), add(rho_rs[0,2,1,3]), sub(rho_rs[0,2,1,3]), add(rho_rs[4,6,5,7]) + for (int j = k; j < k + 4; j++) { + psi_is[j] = simde_mm256_abs_epi16(simde_mm256_subs_epi16(rho_rs[rho_rs_indexes[j]], y1i)); + psi_is[j + 4] = simde_mm256_abs_epi16(simde_mm256_adds_epi16(rho_rs[rho_rs_indexes[j + 4]], y1i)); + } + } + + // Rearrange desired MF output + oai_mm256_separate_real_imag_parts(&y0r, &y0i, stream0_256i_in[i], stream0_256i_in[i + 1]); + + // Rearrange desired channel magnitudes + // [|h|^2(1),|h|^2(2),|h|^2(3),|h|^2(4)]*(2/sqrt(10)) + oai_mm256_separate_real_imag_parts(&ch_mag_des, &xmm2, ch_mag_256i[i], ch_mag_256i[i + 1]); + + // Rearrange interfering channel magnitudes + oai_mm256_separate_real_imag_parts(&ch_mag_int, &xmm2, ch_mag_256i_i[i], ch_mag_256i_i[i + 1]); + + // Scale MF output of desired signal + y0r_over_sqrt10 = simde_mm256_mulhi_epi16(y0r, ONE_OVER_SQRT_10); + y0i_over_sqrt10 = simde_mm256_mulhi_epi16(y0i, ONE_OVER_SQRT_10); + y0r_three_over_sqrt10 = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(y0r, THREE_OVER_SQRT_10), 1); + y0i_three_over_sqrt10 = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(y0i, THREE_OVER_SQRT_10), 1); + + // Compute necessary combination of required terms + y0_s[0] = simde_mm256_adds_epi16(y0r_over_sqrt10, y0i_over_sqrt10); + y0_s[4] = simde_mm256_subs_epi16(y0r_over_sqrt10, y0i_over_sqrt10); + + y0_s[1] = simde_mm256_adds_epi16(y0r_over_sqrt10, y0i_three_over_sqrt10); + y0_s[5] = simde_mm256_subs_epi16(y0r_over_sqrt10, y0i_three_over_sqrt10); + + y0_s[2] = simde_mm256_adds_epi16(y0r_three_over_sqrt10, y0i_over_sqrt10); + y0_s[6] = simde_mm256_subs_epi16(y0r_three_over_sqrt10, y0i_over_sqrt10); + + y0_s[3] = simde_mm256_adds_epi16(y0r_three_over_sqrt10, y0i_three_over_sqrt10); + y0_s[7] = simde_mm256_subs_epi16(y0r_three_over_sqrt10, y0i_three_over_sqrt10); + + for (int j = 0; j < 16; j++) { + // Compute optimal interfering symbol magnitude + a_rs[j] = interference_abs_epi16_256(psi_rs[j], ch_mag_int, ONE_OVER_SQRT_10_Q15, THREE_OVER_SQRT_10); + a_is[j] = interference_abs_epi16_256(psi_is[j], ch_mag_int, ONE_OVER_SQRT_10_Q15, THREE_OVER_SQRT_10); + + // Calculation of groups of two terms in the bit metric involving product of psi and interference magnitude + psi_as[j] = prodsum_psi_a_epi16_256(psi_rs[j], a_rs[j], psi_is[j], a_is[j]); + + // squared interference magnitude times int. ch. power + a_sqs[j] = square_a_epi16_256(a_rs[j], a_is[j], ch_mag_int, SQRT_10_OVER_FOUR); + } + + // Computing different multiples of channel norms + ch_mag_over_10 = simde_mm256_mulhi_epi16(ch_mag_des, ONE_OVER_TWO_SQRT_10); + ch_mag_over_2 = simde_mm256_mulhi_epi16(ch_mag_des, SQRT_10_OVER_FOUR); + ch_mag_over_2 = simde_mm256_slli_epi16(ch_mag_over_2, 1); + ch_mag_9_over_10 = simde_mm256_mulhi_epi16(ch_mag_des, NINE_OVER_TWO_SQRT_10); + ch_mag_9_over_10 = simde_mm256_slli_epi16(ch_mag_9_over_10, 2); + + /// Compute bit metrics (lambda) + + simde__m256i bit_mets[16]; + for (int j = 0; j < 8; j += 4) { + bit_mets[j + 0] = simde_mm256_subs_epi16(psi_as[j + 0], a_sqs[j + 0]); + bit_mets[j + 0] = simde_mm256_adds_epi16(bit_mets[j + 0], y0_s[j + 0]); + bit_mets[j + 0] = simde_mm256_subs_epi16(bit_mets[j + 0], ch_mag_over_10); + + bit_mets[j + 1] = simde_mm256_subs_epi16(psi_as[j + 1], a_sqs[j + 1]); + bit_mets[j + 1] = simde_mm256_adds_epi16(bit_mets[j + 1], y0_s[j + 1]); + bit_mets[j + 1] = simde_mm256_subs_epi16(bit_mets[j + 1], ch_mag_over_2); + bit_mets[j + 2] = simde_mm256_subs_epi16(psi_as[j + 2], a_sqs[j + 2]); + bit_mets[j + 2] = simde_mm256_adds_epi16(bit_mets[j + 2], y0_s[j + 2]); + bit_mets[j + 2] = simde_mm256_subs_epi16(bit_mets[j + 2], ch_mag_over_2); + + bit_mets[j + 3] = simde_mm256_subs_epi16(psi_as[j + 3], a_sqs[j + 3]); + bit_mets[j + 3] = simde_mm256_adds_epi16(bit_mets[j + 3], y0_s[j + 3]); + bit_mets[j + 3] = simde_mm256_subs_epi16(bit_mets[j + 3], ch_mag_9_over_10); + } + for (int j = 8; j < 16; j += 4) { + bit_mets[j + 0] = simde_mm256_subs_epi16(psi_as[j + 0], a_sqs[j + 0]); + bit_mets[j + 0] = simde_mm256_subs_epi16(bit_mets[j + 0], y0_s[(j - 4) & 0x07]); + bit_mets[j + 0] = simde_mm256_subs_epi16(bit_mets[j + 0], ch_mag_over_10); + + bit_mets[j + 1] = simde_mm256_subs_epi16(psi_as[j + 1], a_sqs[j + 1]); + bit_mets[j + 1] = simde_mm256_subs_epi16(bit_mets[j + 1], y0_s[(j - 3) & 0x07]); + bit_mets[j + 1] = simde_mm256_subs_epi16(bit_mets[j + 1], ch_mag_over_2); + bit_mets[j + 2] = simde_mm256_subs_epi16(psi_as[j + 2], a_sqs[j + 2]); + bit_mets[j + 2] = simde_mm256_subs_epi16(bit_mets[j + 2], y0_s[(j - 2) & 0x07]); + bit_mets[j + 2] = simde_mm256_subs_epi16(bit_mets[j + 2], ch_mag_over_2); + + bit_mets[j + 3] = simde_mm256_subs_epi16(psi_as[j + 3], a_sqs[j + 3]); + bit_mets[j + 3] = simde_mm256_subs_epi16(bit_mets[j + 3], y0_s[(j - 1) & 0x07]); + bit_mets[j + 3] = simde_mm256_subs_epi16(bit_mets[j + 3], ch_mag_9_over_10); + } + + /// Compute the LLRs + + // LLR = lambda(c==1) - lambda(c==0) + + // LLR of the first bit: Bit = 1 + simde__m256i logmax_num_re0 = + max_epi16_256(bit_mets[8], bit_mets[9], bit_mets[10], bit_mets[11], bit_mets[12], bit_mets[13], bit_mets[14], bit_mets[15]); + // LLR of the first bit: Bit = 0 + simde__m256i logmax_den_re0 = + max_epi16_256(bit_mets[0], bit_mets[1], bit_mets[2], bit_mets[3], bit_mets[4], bit_mets[5], bit_mets[6], bit_mets[7]); + // LLR of the second bit: Bit = 1 + simde__m256i logmax_num_re1 = + max_epi16_256(bit_mets[4], bit_mets[5], bit_mets[6], bit_mets[7], bit_mets[12], bit_mets[13], bit_mets[14], bit_mets[15]); + // LLR of the second bit: Bit = 0 + simde__m256i logmax_den_re1 = + max_epi16_256(bit_mets[0], bit_mets[1], bit_mets[3], bit_mets[2], bit_mets[8], bit_mets[9], bit_mets[10], bit_mets[11]); + // LLR of the third bit: Bit = 1 + simde__m256i logmax_num_im0 = + max_epi16_256(bit_mets[2], bit_mets[3], bit_mets[6], bit_mets[7], bit_mets[10], bit_mets[11], bit_mets[14], bit_mets[15]); + // LLR of the third bit: Bit = 0 + simde__m256i logmax_den_im0 = + max_epi16_256(bit_mets[0], bit_mets[1], bit_mets[4], bit_mets[5], bit_mets[8], bit_mets[9], bit_mets[12], bit_mets[13]); + // LLR of the fourth bit: Bit = 1 + simde__m256i logmax_num_im1 = + max_epi16_256(bit_mets[1], bit_mets[3], bit_mets[5], bit_mets[7], bit_mets[9], bit_mets[11], bit_mets[13], bit_mets[15]); + // LLR of the fourth bit: Bit = 0 + simde__m256i logmax_den_im1 = + max_epi16_256(bit_mets[0], bit_mets[2], bit_mets[4], bit_mets[6], bit_mets[8], bit_mets[10], bit_mets[12], bit_mets[14]); + + y0r = simde_mm256_subs_epi16(logmax_den_re0, + logmax_num_re0); // LLR of first bit [L1(1), L1(2), L1(3), L1(4), L1(5), L1(6), L1(7), L1(8)...] + y1r = simde_mm256_subs_epi16(logmax_den_re1, + logmax_num_re1); // LLR of second bit [L2(1), L2(2), L2(3), L2(4), L2(5), L2(6), L2(7), L2(8)...] + y0i = simde_mm256_subs_epi16(logmax_den_im0, + logmax_num_im0); // LLR of third bit [L3(1), L3(2), L3(3), L3(4), L3(5), L3(6), L3(7), L3(8)...] + y1i = simde_mm256_subs_epi16(logmax_den_im1, + logmax_num_im1); // LLR of fourth bit [L4(1), L4(2), L4(3), L4(4), L4(5), L4(6), L4(7), L4(8)...] + + // Pack LLRs in output + simde__m128i *y0r_128 = (simde__m128i *)&y0r; + simde__m128i *y1r_128 = (simde__m128i *)&y1r; + simde__m128i *y0i_128 = (simde__m128i *)&y0i; + simde__m128i *y1i_128 = (simde__m128i *)&y1i; + simde__m128i xmm0_128, xmm1_128, xmm2_128, xmm3_128; + xmm0_128 = simde_mm_unpacklo_epi16(y0r_128[0], y1r_128[0]); // [L1(1), L2(1), L1(2), L2(2), L1(3), L2(3), L1(4), L2(4)] + xmm1_128 = simde_mm_unpackhi_epi16(y0r_128[0], y1r_128[0]); // [L1(5), L2(5), L1(6), L2(6), L1(7), L2(7), L1(8), L2(8)] + xmm2_128 = simde_mm_unpacklo_epi16(y0i_128[0], y1i_128[0]); // [L3(1), L4(1), L3(2), L4(2), L3(3), L4(3), L3(4), L4(4)] + xmm3_128 = simde_mm_unpackhi_epi16(y0i_128[0], y1i_128[0]); // [L3(5), L4(5), L3(6), L4(6), L3(7), L4(7), L3(8), L4(8)] + simde__m128i *stream0_128i_out = (simde__m128i *)&stream0_256i_out[2 * i + 0]; + stream0_128i_out[0] = simde_mm_unpacklo_epi32(xmm0_128, xmm2_128); // 8 LLRs, 2 REs + stream0_128i_out[1] = simde_mm_unpackhi_epi32(xmm0_128, xmm2_128); // 8 LLRs, 2 REs + stream0_128i_out[2] = simde_mm_unpacklo_epi32(xmm1_128, xmm3_128); // 8 LLRs, 2 REs + stream0_128i_out[3] = simde_mm_unpackhi_epi32(xmm1_128, xmm3_128); // 8 LLRs, 2 REs + xmm0_128 = simde_mm_unpacklo_epi16(y0r_128[1], y1r_128[1]); // [L1(9), L2(9), L1(10), L2(10), L1(11), L2(11), L1(12), L2(12)] + xmm1_128 = simde_mm_unpackhi_epi16(y0r_128[1], y1r_128[1]); // [L1(13), L2(13), L1(14), L2(14), L1(15), L2(15), L1(16), L2(16)] + xmm2_128 = simde_mm_unpacklo_epi16(y0i_128[1], y1i_128[1]); // [L3(9), L4(9), L3(10), L4(10), L3(11), L4(11), L3(12), L4(12)] + xmm3_128 = simde_mm_unpackhi_epi16(y0i_128[1], y1i_128[1]); // [L3(13), L4(13), L3(14), L4(14), L3(15), L4(15), L3(16), L4(16)] + stream0_128i_out = (simde__m128i *)&stream0_256i_out[2 * i + 2]; + stream0_128i_out[0] = simde_mm_unpacklo_epi32(xmm0_128, xmm2_128); // 8 LLRs, 2 REs + stream0_128i_out[1] = simde_mm_unpackhi_epi32(xmm0_128, xmm2_128); // 8 LLRs, 2 REs + stream0_128i_out[2] = simde_mm_unpacklo_epi32(xmm1_128, xmm3_128); // 8 LLRs, 2 REs + stream0_128i_out[3] = simde_mm_unpackhi_epi32(xmm1_128, xmm3_128); // 8 LLRs, 2 REs + } +#endif +} + +/* + * This function computes the LLRs of stream 0 (s_0) in presence of the interfering stream 1 (s_1) assuming that both symbols are + * 64QAM. It can be used for both MU-MIMO interference-aware receiver or for SU-MIMO receivers. + * + * Input: + * stream0_in: MF filter output for 1st stream, i.e., y0' = h0'*y0 + * stream1_in: MF filter output for 2nd stream, i.e., y1' = h1'*y0 + * ch_mag: 4*h0/sqrt(42), [Re0 Im0 Re1 Im1] s.t. Im0=Re0, Im1=Re1, etc + * ch_mag_i: 4*h0/sqrt(42), [Re0 Im0 Re1 Im1] s.t. Im0=Re0, Im1=Re1, etc + * rho01: Channel cross correlation, i.e., rho01 = h0'*h1 + * length: Number of resource elements + * + * Output: + * stream0_out: Output LLRs for 1st stream + */ +void nr_qam64_llr_2layer(c16_t *stream0_in, + c16_t *stream1_in, + c16_t *ch_mag, + c16_t *ch_mag_i, + int16_t *stream0_out, + c16_t *rho01, + uint32_t length) +{ +#ifdef USE_128BIT + simde__m128i *rho01_128i = (simde__m128i *)rho01; + simde__m128i *stream0_128i_in = (simde__m128i *)stream0_in; + simde__m128i *stream1_128i_in = (simde__m128i *)stream1_in; + simde__m128i *ch_mag_128i = (simde__m128i *)ch_mag; + simde__m128i *ch_mag_128i_i = (simde__m128i *)ch_mag_i; + + simde__m128i ONE_OVER_SQRT_42 = simde_mm_set1_epi16(10112); // round(1/sqrt(42)*2^16) + simde__m128i THREE_OVER_SQRT_42 = simde_mm_set1_epi16(30337); // round(3/sqrt(42)*2^16) + simde__m128i FIVE_OVER_SQRT_42 = simde_mm_set1_epi16(25281); // round(5/sqrt(42)*2^15) + simde__m128i SEVEN_OVER_SQRT_42 = simde_mm_set1_epi16(17697); // round(7/sqrt(42)*2^14) Q2.14 + simde__m128i ONE_OVER_SQRT_2 = simde_mm_set1_epi16(23170); // round(1/sqrt(2)*2^15) + simde__m128i ONE_OVER_SQRT_2_42 = simde_mm_set1_epi16(3575); // round(1/sqrt(2*42)*2^15) + simde__m128i THREE_OVER_SQRT_2_42 = simde_mm_set1_epi16(10726); // round(3/sqrt(2*42)*2^15) + simde__m128i FIVE_OVER_SQRT_2_42 = simde_mm_set1_epi16(17876); // round(5/sqrt(2*42)*2^15) + simde__m128i SEVEN_OVER_SQRT_2_42 = simde_mm_set1_epi16(25027); // round(7/sqrt(2*42)*2^15) + simde__m128i FORTYNINE_OVER_FOUR_SQRT_42 = simde_mm_set1_epi16(30969); // round(49/(4*sqrt(42))*2^14), Q2.14 + simde__m128i THIRTYSEVEN_OVER_FOUR_SQRT_42 = simde_mm_set1_epi16(23385); // round(37/(4*sqrt(42))*2^14), Q2.14 + simde__m128i TWENTYFIVE_OVER_FOUR_SQRT_42 = simde_mm_set1_epi16(31601); // round(25/(4*sqrt(42))*2^15) + simde__m128i TWENTYNINE_OVER_FOUR_SQRT_42 = simde_mm_set1_epi16(18329); // round(29/(4*sqrt(42))*2^15), Q2.14 + simde__m128i SEVENTEEN_OVER_FOUR_SQRT_42 = simde_mm_set1_epi16(21489); // round(17/(4*sqrt(42))*2^15) + simde__m128i NINE_OVER_FOUR_SQRT_42 = simde_mm_set1_epi16(11376); // round(9/(4*sqrt(42))*2^15) + simde__m128i THIRTEEN_OVER_FOUR_SQRT_42 = simde_mm_set1_epi16(16433); // round(13/(4*sqrt(42))*2^15) + simde__m128i FIVE_OVER_FOUR_SQRT_42 = simde_mm_set1_epi16(6320); // round(5/(4*sqrt(42))*2^15) + simde__m128i ONE_OVER_FOUR_SQRT_42 = simde_mm_set1_epi16(1264); // round(1/(4*sqrt(42))*2^15) + simde__m128i SQRT_42_OVER_FOUR = simde_mm_set1_epi16(13272); // round(sqrt(42)/4*2^13), Q3.12 + + simde__m128i ch_mag_des; + simde__m128i ch_mag_int; + simde__m128i y0r_one_over_sqrt_21; + simde__m128i y0r_three_over_sqrt_21; + simde__m128i y0r_five_over_sqrt_21; + simde__m128i y0r_seven_over_sqrt_21; + simde__m128i y0i_one_over_sqrt_21; + simde__m128i y0i_three_over_sqrt_21; + simde__m128i y0i_five_over_sqrt_21; + simde__m128i y0i_seven_over_sqrt_21; + simde__m128i ch_mag_int_with_sigma2; + simde__m128i two_ch_mag_int_with_sigma2; + simde__m128i three_ch_mag_int_with_sigma2; + + for (int i = 0; i < length >> 2; i += 2) { + // Get rho + simde__m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8; + oai_mm_separate_real_imag_parts(&xmm2, &xmm3, rho01_128i[i], rho01_128i[i + 1]); + + simde__m128i rho_rpi = simde_mm_adds_epi16(xmm2, xmm3); // rho = Re(rho) + Im(rho) + simde__m128i rho_rmi = simde_mm_subs_epi16(xmm2, xmm3); // rho* = Re(rho) - Im(rho) + + // Compute the different rhos + simde__m128i rho_rs[32]; + rho_rs[27] = simde_mm_mulhi_epi16(rho_rpi, ONE_OVER_SQRT_42); + rho_rs[28] = simde_mm_mulhi_epi16(rho_rmi, ONE_OVER_SQRT_42); + rho_rs[18] = simde_mm_mulhi_epi16(rho_rpi, THREE_OVER_SQRT_42); + rho_rs[21] = simde_mm_mulhi_epi16(rho_rmi, THREE_OVER_SQRT_42); + rho_rs[9] = simde_mm_mulhi_epi16(rho_rpi, FIVE_OVER_SQRT_42); + rho_rs[14] = simde_mm_mulhi_epi16(rho_rmi, FIVE_OVER_SQRT_42); + rho_rs[0] = simde_mm_mulhi_epi16(rho_rpi, SEVEN_OVER_SQRT_42); + rho_rs[7] = simde_mm_mulhi_epi16(rho_rmi, SEVEN_OVER_SQRT_42); + + rho_rs[9] = simde_mm_slli_epi16(rho_rs[9], 1); + rho_rs[14] = simde_mm_slli_epi16(rho_rs[14], 1); + rho_rs[0] = simde_mm_slli_epi16(rho_rs[0], 2); + rho_rs[7] = simde_mm_slli_epi16(rho_rs[7], 2); + + xmm4 = simde_mm_mulhi_epi16(xmm2, ONE_OVER_SQRT_42); + xmm5 = simde_mm_mulhi_epi16(xmm3, ONE_OVER_SQRT_42); + xmm6 = simde_mm_mulhi_epi16(xmm3, THREE_OVER_SQRT_42); + xmm7 = simde_mm_mulhi_epi16(xmm3, FIVE_OVER_SQRT_42); + xmm8 = simde_mm_mulhi_epi16(xmm3, SEVEN_OVER_SQRT_42); + xmm7 = simde_mm_slli_epi16(xmm7, 1); + xmm8 = simde_mm_slli_epi16(xmm8, 2); + + rho_rs[26] = simde_mm_adds_epi16(xmm4, xmm6); + rho_rs[29] = simde_mm_subs_epi16(xmm4, xmm6); + rho_rs[25] = simde_mm_adds_epi16(xmm4, xmm7); + rho_rs[30] = simde_mm_subs_epi16(xmm4, xmm7); + rho_rs[24] = simde_mm_adds_epi16(xmm4, xmm8); + rho_rs[31] = simde_mm_subs_epi16(xmm4, xmm8); + + xmm4 = simde_mm_mulhi_epi16(xmm2, THREE_OVER_SQRT_42); + rho_rs[19] = simde_mm_adds_epi16(xmm4, xmm5); + rho_rs[20] = simde_mm_subs_epi16(xmm4, xmm5); + rho_rs[17] = simde_mm_adds_epi16(xmm4, xmm7); + rho_rs[22] = simde_mm_subs_epi16(xmm4, xmm7); + rho_rs[16] = simde_mm_adds_epi16(xmm4, xmm8); + rho_rs[23] = simde_mm_subs_epi16(xmm4, xmm8); + + xmm4 = simde_mm_mulhi_epi16(xmm2, FIVE_OVER_SQRT_42); + xmm4 = simde_mm_slli_epi16(xmm4, 1); + rho_rs[11] = simde_mm_adds_epi16(xmm4, xmm5); + rho_rs[12] = simde_mm_subs_epi16(xmm4, xmm5); + rho_rs[10] = simde_mm_adds_epi16(xmm4, xmm6); + rho_rs[13] = simde_mm_subs_epi16(xmm4, xmm6); + rho_rs[8] = simde_mm_adds_epi16(xmm4, xmm8); + rho_rs[15] = simde_mm_subs_epi16(xmm4, xmm8); + + xmm4 = simde_mm_mulhi_epi16(xmm2, SEVEN_OVER_SQRT_42); + xmm4 = simde_mm_slli_epi16(xmm4, 2); + rho_rs[3] = simde_mm_adds_epi16(xmm4, xmm5); + rho_rs[4] = simde_mm_subs_epi16(xmm4, xmm5); + rho_rs[2] = simde_mm_adds_epi16(xmm4, xmm6); + rho_rs[5] = simde_mm_subs_epi16(xmm4, xmm6); + rho_rs[1] = simde_mm_adds_epi16(xmm4, xmm7); + rho_rs[6] = simde_mm_subs_epi16(xmm4, xmm7); + + // Rearrange interfering MF output + simde__m128i y1r, y1i; + oai_mm_separate_real_imag_parts(&y1r, &y1i, stream1_128i_in[i], stream1_128i_in[i + 1]); + + // Psi_r calculation from rho_rpi or rho_rmi + xmm0 = simde_mm_set1_epi16(0); // ZERO for abs_pi16 + xmm2 = simde_mm_subs_epi16(rho_rs[0], y1r); + + simde__m128i psi_r_s[64]; + for (int j = 0; j < 32; j++) // psi_r_s[0~31], rho_rs[0~31] + psi_r_s[j] = simde_mm_abs_epi16(simde_mm_subs_epi16(rho_rs[j], y1r)); + for (int j = 32; j < 64; j++) // psi_r_s[32~64], rho_rs[31~0] + psi_r_s[j] = simde_mm_abs_epi16(simde_mm_adds_epi16(rho_rs[63 - j], y1r)); + + // simde__m128i psi_i calculation from rho_rpi or rho_rmi + simde__m128i psi_i_s[64]; + const uint8_t rho_rs_index[32] = {7, 15, 23, 31, 24, 16, 8, 0, 6, 14, 22, 30, 25, 17, 9, 1, + 5, 13, 21, 29, 26, 18, 10, 2, 4, 12, 20, 28, 27, 19, 11, 3}; + for (int k = 0; k < 32; k += 8) { // psi_i_s[0~31] + for (int j = k; j < k + 4; j++) + psi_i_s[j] = simde_mm_abs_epi16(simde_mm_subs_epi16(rho_rs[rho_rs_index[j]], y1i)); + for (int j = k + 4; j < k + 8; j++) + psi_i_s[j] = simde_mm_abs_epi16(simde_mm_adds_epi16(rho_rs[rho_rs_index[j]], y1i)); + } + for (int k = 32; k < 64; k += 8) { // psi_i_s[32~64] + for (int j = k; j < k + 4; j++) + psi_i_s[j] = simde_mm_abs_epi16(simde_mm_subs_epi16(rho_rs[rho_rs_index[63 - j]], y1i)); + for (int j = k + 4; j < k + 8; j++) + psi_i_s[j] = simde_mm_abs_epi16(simde_mm_adds_epi16(rho_rs[rho_rs_index[63 - j]], y1i)); + } + + // Rearrange desired MF output + simde__m128i y0r, y0i; + oai_mm_separate_real_imag_parts(&y0r, &y0i, stream0_128i_in[i], stream0_128i_in[i + 1]); + + // Rearrange desired channel magnitudes + // [|h|^2(1),|h|^2(1),|h|^2(2),|h|^2(2),...,,|h|^2(7),|h|^2(7)]*(2/sqrt(10)) + // xmm2 is dummy variable that contains the same values as ch_mag_des + oai_mm_separate_real_imag_parts(&ch_mag_des, &xmm2, ch_mag_128i[i], ch_mag_128i[i + 1]); + + // Rearrange interfering channel magnitudes + oai_mm_separate_real_imag_parts(&ch_mag_int, &xmm2, ch_mag_128i_i[i], ch_mag_128i_i[i + 1]); + + y0r_one_over_sqrt_21 = simde_mm_mulhi_epi16(y0r, ONE_OVER_SQRT_42); + y0r_three_over_sqrt_21 = simde_mm_mulhi_epi16(y0r, THREE_OVER_SQRT_42); + y0r_five_over_sqrt_21 = simde_mm_mulhi_epi16(y0r, FIVE_OVER_SQRT_42); + y0r_five_over_sqrt_21 = simde_mm_slli_epi16(y0r_five_over_sqrt_21, 1); + y0r_seven_over_sqrt_21 = simde_mm_mulhi_epi16(y0r, SEVEN_OVER_SQRT_42); + y0r_seven_over_sqrt_21 = simde_mm_slli_epi16(y0r_seven_over_sqrt_21, 2); // Q2.14 + + y0i_one_over_sqrt_21 = simde_mm_mulhi_epi16(y0i, ONE_OVER_SQRT_42); + y0i_three_over_sqrt_21 = simde_mm_mulhi_epi16(y0i, THREE_OVER_SQRT_42); + y0i_five_over_sqrt_21 = simde_mm_mulhi_epi16(y0i, FIVE_OVER_SQRT_42); + y0i_five_over_sqrt_21 = simde_mm_slli_epi16(y0i_five_over_sqrt_21, 1); + y0i_seven_over_sqrt_21 = simde_mm_mulhi_epi16(y0i, SEVEN_OVER_SQRT_42); + y0i_seven_over_sqrt_21 = simde_mm_slli_epi16(y0i_seven_over_sqrt_21, 2); // Q2.14 + + simde__m128i y0_s[64]; + const simde__m128i y0r_over_s[8] = {y0r_seven_over_sqrt_21, + y0r_five_over_sqrt_21, + y0r_three_over_sqrt_21, + y0r_one_over_sqrt_21}; + for (int j = 0; j < 32; j += 8) { + y0_s[j + 0] = simde_mm_adds_epi16(y0r_over_s[j >> 3], y0i_seven_over_sqrt_21); + y0_s[j + 1] = simde_mm_adds_epi16(y0r_over_s[j >> 3], y0i_five_over_sqrt_21); + y0_s[j + 2] = simde_mm_adds_epi16(y0r_over_s[j >> 3], y0i_three_over_sqrt_21); + y0_s[j + 3] = simde_mm_adds_epi16(y0r_over_s[j >> 3], y0i_one_over_sqrt_21); + y0_s[j + 4] = simde_mm_subs_epi16(y0r_over_s[j >> 3], y0i_one_over_sqrt_21); + y0_s[j + 5] = simde_mm_subs_epi16(y0r_over_s[j >> 3], y0i_three_over_sqrt_21); + y0_s[j + 6] = simde_mm_subs_epi16(y0r_over_s[j >> 3], y0i_five_over_sqrt_21); + y0_s[j + 7] = simde_mm_subs_epi16(y0r_over_s[j >> 3], y0i_seven_over_sqrt_21); + } + + ch_mag_int_with_sigma2 = simde_mm_srai_epi16(ch_mag_int, 1); // *2 + two_ch_mag_int_with_sigma2 = ch_mag_int; // *4 + three_ch_mag_int_with_sigma2 = simde_mm_adds_epi16(ch_mag_int_with_sigma2, two_ch_mag_int_with_sigma2); // *6 + simde__m128i a_r_s[64]; + simde__m128i a_i_s[64]; + simde__m128i psi_a_s[64]; + simde__m128i a_sq_s[64]; + for (int j = 0; j < 64; j++) { + // Detection of interference term + a_r_s[j] = interference_abs_64qam_epi16(psi_r_s[j], + ch_mag_int_with_sigma2, + two_ch_mag_int_with_sigma2, + three_ch_mag_int_with_sigma2, + ONE_OVER_SQRT_2_42, + THREE_OVER_SQRT_2_42, + FIVE_OVER_SQRT_2_42, + SEVEN_OVER_SQRT_2_42); + a_i_s[j] = interference_abs_64qam_epi16(psi_i_s[j], + ch_mag_int_with_sigma2, + two_ch_mag_int_with_sigma2, + three_ch_mag_int_with_sigma2, + ONE_OVER_SQRT_2_42, + THREE_OVER_SQRT_2_42, + FIVE_OVER_SQRT_2_42, + SEVEN_OVER_SQRT_2_42); + + // Calculation of a group of two terms in the bit metric involving product of psi and interference + psi_a_s[j] = prodsum_psi_a_epi16(psi_r_s[j], a_r_s[j], psi_i_s[j], a_i_s[j]); + + // Multiply by sqrt(2) + psi_a_s[j] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(psi_a_s[j], ONE_OVER_SQRT_2), 2); + + // Calculation of a group of two terms in the bit metric involving squares of interference + a_sq_s[j] = square_a_64qam_epi16(a_r_s[j], a_i_s[j], ch_mag_int, SQRT_42_OVER_FOUR); + } + + // Computing different multiples of ||h0||^2 + simde__m128i ch_mag_with_sigma2[10]; + enum ch_mag_over_42with_sigma2_vals { mag2 = 0, mag10, mag26, mag18, mag34, mag58, mag50, mag74, mag98 }; + // x=1, y=1 + ch_mag_with_sigma2[mag2] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, ONE_OVER_FOUR_SQRT_42), 1); + // x=1, y=3 + ch_mag_with_sigma2[mag10] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, FIVE_OVER_FOUR_SQRT_42), 1); + // x=1, x=5 + ch_mag_with_sigma2[mag26] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, THIRTEEN_OVER_FOUR_SQRT_42), 1); + // x=1, y=7 + ch_mag_with_sigma2[mag50] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, TWENTYFIVE_OVER_FOUR_SQRT_42), 1); + // x=3, y=3 + ch_mag_with_sigma2[mag18] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, NINE_OVER_FOUR_SQRT_42), 1); + // x=3, y=5 + ch_mag_with_sigma2[mag34] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, SEVENTEEN_OVER_FOUR_SQRT_42), 1); + // x=3, y=7 + ch_mag_with_sigma2[mag58] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, TWENTYNINE_OVER_FOUR_SQRT_42), 2); + // x=5, y=5 + ch_mag_with_sigma2[mag50] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, TWENTYFIVE_OVER_FOUR_SQRT_42), 1); + // x=5, y=7 + ch_mag_with_sigma2[mag74] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, THIRTYSEVEN_OVER_FOUR_SQRT_42), 2); + // x=7, y=7 + ch_mag_with_sigma2[mag98] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(ch_mag_des, FORTYNINE_OVER_FOUR_SQRT_42), 2); + + // Computing Metrics + simde__m128i bit_met_s[64]; + const enum ch_mag_over_42with_sigma2_vals table[] = { + mag98, mag74, mag58, mag50, mag50, mag58, mag74, mag98, mag74, mag50, mag34, mag26, mag26, mag34, mag50, mag74, + mag58, mag34, mag18, mag10, mag10, mag18, mag34, mag58, mag50, mag26, mag10, mag2, mag2, mag10, mag26, mag50}; + + for (int i = 0; i < 32; i++) { + const simde__m128i x = simde_mm_adds_epi16(simde_mm_subs_epi16(psi_a_s[i], a_sq_s[i]), y0_s[i]); + bit_met_s[i] = simde_mm_subs_epi16(x, ch_mag_with_sigma2[table[i]]); + } + for (int i = 0; i < 32; i++) { + const simde__m128i x = simde_mm_subs_epi16(simde_mm_subs_epi16(psi_a_s[32 + i], a_sq_s[32 + i]), y0_s[31 - i]); + bit_met_s[32 + i] = simde_mm_subs_epi16(x, ch_mag_with_sigma2[table[31 - i]]); + } + + // Detection for bits + simde__m128i logmax_den_re0; + simde__m128i logmax_num_re0; + // Detection for 1st bit + // bit = 1 + xmm0 = max_epi16(bit_met_s[56], + bit_met_s[57], + bit_met_s[58], + bit_met_s[59], + bit_met_s[60], + bit_met_s[61], + bit_met_s[62], + bit_met_s[63]); + xmm1 = max_epi16(bit_met_s[48], + bit_met_s[49], + bit_met_s[50], + bit_met_s[51], + bit_met_s[52], + bit_met_s[53], + bit_met_s[54], + bit_met_s[55]); + xmm2 = max_epi16(bit_met_s[40], + bit_met_s[41], + bit_met_s[42], + bit_met_s[43], + bit_met_s[44], + bit_met_s[45], + bit_met_s[46], + bit_met_s[47]); + xmm3 = max_epi16(bit_met_s[32], + bit_met_s[33], + bit_met_s[34], + bit_met_s[35], + bit_met_s[36], + bit_met_s[37], + bit_met_s[38], + bit_met_s[39]); + logmax_den_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); + + // bit = 0 + xmm0 = + max_epi16(bit_met_s[0], bit_met_s[1], bit_met_s[2], bit_met_s[3], bit_met_s[4], bit_met_s[5], bit_met_s[6], bit_met_s[7]); + xmm1 = max_epi16(bit_met_s[8], + bit_met_s[9], + bit_met_s[10], + bit_met_s[11], + bit_met_s[12], + bit_met_s[13], + bit_met_s[14], + bit_met_s[15]); + xmm2 = max_epi16(bit_met_s[16], + bit_met_s[17], + bit_met_s[18], + bit_met_s[19], + bit_met_s[20], + bit_met_s[21], + bit_met_s[22], + bit_met_s[23]); + xmm3 = max_epi16(bit_met_s[24], + bit_met_s[25], + bit_met_s[26], + bit_met_s[27], + bit_met_s[28], + bit_met_s[29], + bit_met_s[30], + bit_met_s[31]); + logmax_num_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); + + y0r = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); + + // Detection for 2nd bit + // bit = 1 + xmm0 = max_epi16(bit_met_s[4], + bit_met_s[12], + bit_met_s[20], + bit_met_s[28], + bit_met_s[36], + bit_met_s[44], + bit_met_s[52], + bit_met_s[60]); + xmm1 = max_epi16(bit_met_s[5], + bit_met_s[13], + bit_met_s[21], + bit_met_s[29], + bit_met_s[37], + bit_met_s[45], + bit_met_s[53], + bit_met_s[61]); + xmm2 = max_epi16(bit_met_s[6], + bit_met_s[14], + bit_met_s[22], + bit_met_s[30], + bit_met_s[38], + bit_met_s[46], + bit_met_s[54], + bit_met_s[62]); + xmm3 = max_epi16(bit_met_s[7], + bit_met_s[15], + bit_met_s[23], + bit_met_s[31], + bit_met_s[39], + bit_met_s[47], + bit_met_s[55], + bit_met_s[63]); + logmax_den_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); + + // bit = 0 + xmm0 = max_epi16(bit_met_s[3], + bit_met_s[11], + bit_met_s[19], + bit_met_s[27], + bit_met_s[35], + bit_met_s[43], + bit_met_s[51], + bit_met_s[59]); + xmm1 = max_epi16(bit_met_s[2], + bit_met_s[10], + bit_met_s[18], + bit_met_s[26], + bit_met_s[34], + bit_met_s[42], + bit_met_s[50], + bit_met_s[58]); + xmm2 = max_epi16(bit_met_s[1], + bit_met_s[9], + bit_met_s[17], + bit_met_s[25], + bit_met_s[33], + bit_met_s[41], + bit_met_s[49], + bit_met_s[57]); + xmm3 = max_epi16(bit_met_s[0], + bit_met_s[8], + bit_met_s[16], + bit_met_s[24], + bit_met_s[32], + bit_met_s[40], + bit_met_s[48], + bit_met_s[56]); + logmax_num_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); + + y1r = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); + + // Detection for 3rd bit + xmm0 = max_epi16(bit_met_s[63], + bit_met_s[62], + bit_met_s[61], + bit_met_s[60], + bit_met_s[59], + bit_met_s[58], + bit_met_s[57], + bit_met_s[56]); + xmm1 = max_epi16(bit_met_s[55], + bit_met_s[54], + bit_met_s[53], + bit_met_s[52], + bit_met_s[51], + bit_met_s[50], + bit_met_s[49], + bit_met_s[48]); + xmm2 = max_epi16(bit_met_s[15], + bit_met_s[14], + bit_met_s[13], + bit_met_s[12], + bit_met_s[11], + bit_met_s[10], + bit_met_s[9], + bit_met_s[8]); + xmm3 = + max_epi16(bit_met_s[7], bit_met_s[6], bit_met_s[5], bit_met_s[4], bit_met_s[3], bit_met_s[2], bit_met_s[1], bit_met_s[0]); + logmax_den_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); + + xmm0 = max_epi16(bit_met_s[47], + bit_met_s[46], + bit_met_s[45], + bit_met_s[44], + bit_met_s[43], + bit_met_s[42], + bit_met_s[41], + bit_met_s[40]); + xmm1 = max_epi16(bit_met_s[39], + bit_met_s[38], + bit_met_s[37], + bit_met_s[36], + bit_met_s[35], + bit_met_s[34], + bit_met_s[33], + bit_met_s[32]); + xmm2 = max_epi16(bit_met_s[31], + bit_met_s[30], + bit_met_s[29], + bit_met_s[28], + bit_met_s[27], + bit_met_s[26], + bit_met_s[25], + bit_met_s[24]); + xmm3 = max_epi16(bit_met_s[23], + bit_met_s[22], + bit_met_s[21], + bit_met_s[20], + bit_met_s[19], + bit_met_s[18], + bit_met_s[17], + bit_met_s[16]); + logmax_num_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); + + simde__m128i y2r = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); + + // Detection for 4th bit + xmm0 = max_epi16(bit_met_s[0], + bit_met_s[8], + bit_met_s[16], + bit_met_s[24], + bit_met_s[32], + bit_met_s[40], + bit_met_s[48], + bit_met_s[56]); + xmm1 = max_epi16(bit_met_s[1], + bit_met_s[9], + bit_met_s[17], + bit_met_s[25], + bit_met_s[33], + bit_met_s[41], + bit_met_s[49], + bit_met_s[57]); + xmm2 = max_epi16(bit_met_s[6], + bit_met_s[14], + bit_met_s[22], + bit_met_s[30], + bit_met_s[38], + bit_met_s[46], + bit_met_s[54], + bit_met_s[62]); + xmm3 = max_epi16(bit_met_s[7], + bit_met_s[15], + bit_met_s[23], + bit_met_s[31], + bit_met_s[39], + bit_met_s[47], + bit_met_s[55], + bit_met_s[63]); + logmax_den_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); + + xmm0 = max_epi16(bit_met_s[4], + bit_met_s[12], + bit_met_s[20], + bit_met_s[28], + bit_met_s[36], + bit_met_s[44], + bit_met_s[52], + bit_met_s[60]); + xmm1 = max_epi16(bit_met_s[5], + bit_met_s[13], + bit_met_s[21], + bit_met_s[29], + bit_met_s[37], + bit_met_s[45], + bit_met_s[53], + bit_met_s[61]); + xmm2 = max_epi16(bit_met_s[3], + bit_met_s[11], + bit_met_s[19], + bit_met_s[27], + bit_met_s[35], + bit_met_s[43], + bit_met_s[51], + bit_met_s[59]); + xmm3 = max_epi16(bit_met_s[2], + bit_met_s[10], + bit_met_s[18], + bit_met_s[26], + bit_met_s[34], + bit_met_s[42], + bit_met_s[50], + bit_met_s[58]); + logmax_num_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); + + y0i = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); + + // Detection for 5th bit + xmm0 = max_epi16(bit_met_s[63], + bit_met_s[62], + bit_met_s[61], + bit_met_s[60], + bit_met_s[59], + bit_met_s[58], + bit_met_s[57], + bit_met_s[56]); + xmm1 = max_epi16(bit_met_s[39], + bit_met_s[38], + bit_met_s[37], + bit_met_s[36], + bit_met_s[35], + bit_met_s[34], + bit_met_s[33], + bit_met_s[32]); + xmm2 = max_epi16(bit_met_s[31], + bit_met_s[30], + bit_met_s[29], + bit_met_s[28], + bit_met_s[27], + bit_met_s[26], + bit_met_s[25], + bit_met_s[24]); + xmm3 = + max_epi16(bit_met_s[7], bit_met_s[6], bit_met_s[5], bit_met_s[4], bit_met_s[3], bit_met_s[2], bit_met_s[1], bit_met_s[0]); + logmax_den_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); + + xmm0 = max_epi16(bit_met_s[55], + bit_met_s[54], + bit_met_s[53], + bit_met_s[52], + bit_met_s[51], + bit_met_s[50], + bit_met_s[49], + bit_met_s[48]); + xmm1 = max_epi16(bit_met_s[47], + bit_met_s[46], + bit_met_s[45], + bit_met_s[44], + bit_met_s[43], + bit_met_s[42], + bit_met_s[41], + bit_met_s[40]); + xmm2 = max_epi16(bit_met_s[23], + bit_met_s[22], + bit_met_s[21], + bit_met_s[20], + bit_met_s[19], + bit_met_s[18], + bit_met_s[17], + bit_met_s[16]); + xmm3 = max_epi16(bit_met_s[15], + bit_met_s[14], + bit_met_s[13], + bit_met_s[12], + bit_met_s[11], + bit_met_s[10], + bit_met_s[9], + bit_met_s[8]); + logmax_num_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); + + y1i = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); + + // Detection for 6th bit + xmm0 = max_epi16(bit_met_s[0], + bit_met_s[8], + bit_met_s[16], + bit_met_s[24], + bit_met_s[32], + bit_met_s[40], + bit_met_s[48], + bit_met_s[56]); + xmm1 = max_epi16(bit_met_s[3], + bit_met_s[11], + bit_met_s[19], + bit_met_s[27], + bit_met_s[35], + bit_met_s[43], + bit_met_s[51], + bit_met_s[59]); + xmm2 = max_epi16(bit_met_s[4], + bit_met_s[12], + bit_met_s[20], + bit_met_s[28], + bit_met_s[36], + bit_met_s[44], + bit_met_s[52], + bit_met_s[60]); + xmm3 = max_epi16(bit_met_s[7], + bit_met_s[15], + bit_met_s[23], + bit_met_s[31], + bit_met_s[39], + bit_met_s[47], + bit_met_s[55], + bit_met_s[63]); + logmax_den_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); + + xmm0 = max_epi16(bit_met_s[6], + bit_met_s[14], + bit_met_s[22], + bit_met_s[30], + bit_met_s[38], + bit_met_s[46], + bit_met_s[54], + bit_met_s[62]); + xmm1 = max_epi16(bit_met_s[5], + bit_met_s[13], + bit_met_s[21], + bit_met_s[29], + bit_met_s[37], + bit_met_s[45], + bit_met_s[53], + bit_met_s[61]); + xmm2 = max_epi16(bit_met_s[2], + bit_met_s[10], + bit_met_s[18], + bit_met_s[26], + bit_met_s[34], + bit_met_s[42], + bit_met_s[50], + bit_met_s[58]); + xmm3 = max_epi16(bit_met_s[1], + bit_met_s[9], + bit_met_s[17], + bit_met_s[25], + bit_met_s[33], + bit_met_s[41], + bit_met_s[49], + bit_met_s[57]); + logmax_num_re0 = simde_mm_max_epi16(simde_mm_max_epi16(xmm0, xmm1), simde_mm_max_epi16(xmm2, xmm3)); + + simde__m128i y2i = simde_mm_subs_epi16(logmax_num_re0, logmax_den_re0); + + // Map to output stream, difficult to do in SIMD since we have 6 16bit LLRs + for (int re = 0; re < 8; re++) { + *stream0_out++ = ((short *)&y0r)[re]; + *stream0_out++ = ((short *)&y1r)[re]; + *stream0_out++ = ((short *)&y2r)[re]; + *stream0_out++ = ((short *)&y0i)[re]; + *stream0_out++ = ((short *)&y1i)[re]; + *stream0_out++ = ((short *)&y2i)[re]; + } + } +#else + simde__m256i *rho01_256i = (simde__m256i *)rho01; + simde__m256i *stream0_256i_in = (simde__m256i *)stream0_in; + simde__m256i *stream1_256i_in = (simde__m256i *)stream1_in; + simde__m256i *ch_mag_256i = (simde__m256i *)ch_mag; + simde__m256i *ch_mag_256i_i = (simde__m256i *)ch_mag_i; + + simde__m256i ONE_OVER_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(10112)); // round(1/sqrt(42)*2^16) + simde__m256i THREE_OVER_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(30337)); // round(3/sqrt(42)*2^16) + simde__m256i FIVE_OVER_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(25281)); // round(5/sqrt(42)*2^15) + simde__m256i SEVEN_OVER_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(17697)); // round(7/sqrt(42)*2^14) Q2.14 + simde__m256i ONE_OVER_SQRT_2 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(23170)); // round(1/sqrt(2)*2^15) + simde__m256i ONE_OVER_SQRT_2_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(3575)); // round(1/sqrt(2*42)*2^15) + simde__m256i THREE_OVER_SQRT_2_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(10726)); // round(3/sqrt(2*42)*2^15) + simde__m256i FIVE_OVER_SQRT_2_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(17876)); // round(5/sqrt(2*42)*2^15) + simde__m256i SEVEN_OVER_SQRT_2_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(25027)); // round(7/sqrt(2*42)*2^15) + simde__m256i FORTYNINE_OVER_FOUR_SQRT_42 = + simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(30969)); // round(49/(4*sqrt(42))*2^14), Q2.14 + simde__m256i THIRTYSEVEN_OVER_FOUR_SQRT_42 = + simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(23385)); // round(37/(4*sqrt(42))*2^14), Q2.14 + simde__m256i TWENTYFIVE_OVER_FOUR_SQRT_42 = + simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(31601)); // round(25/(4*sqrt(42))*2^15) + simde__m256i TWENTYNINE_OVER_FOUR_SQRT_42 = + simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(18329)); // round(29/(4*sqrt(42))*2^15), Q2.14 + simde__m256i SEVENTEEN_OVER_FOUR_SQRT_42 = + simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(21489)); // round(17/(4*sqrt(42))*2^15) + simde__m256i NINE_OVER_FOUR_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(11376)); // round(9/(4*sqrt(42))*2^15) + simde__m256i THIRTEEN_OVER_FOUR_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(16433)); // round(13/(4*sqrt(42))*2^15) + simde__m256i FIVE_OVER_FOUR_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(6320)); // round(5/(4*sqrt(42))*2^15) + simde__m256i ONE_OVER_FOUR_SQRT_42 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(1264)); // round(1/(4*sqrt(42))*2^15) + simde__m256i SQRT_42_OVER_FOUR = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(13272)); // round(sqrt(42)/4*2^13), Q3.12 + + simde__m256i ch_mag_des; + simde__m256i ch_mag_int; + simde__m256i y0r_one_over_sqrt_21; + simde__m256i y0r_three_over_sqrt_21; + simde__m256i y0r_five_over_sqrt_21; + simde__m256i y0r_seven_over_sqrt_21; + simde__m256i y0i_one_over_sqrt_21; + simde__m256i y0i_three_over_sqrt_21; + simde__m256i y0i_five_over_sqrt_21; + simde__m256i y0i_seven_over_sqrt_21; + simde__m256i ch_mag_int_with_sigma2; + simde__m256i two_ch_mag_int_with_sigma2; + simde__m256i three_ch_mag_int_with_sigma2; + + uint32_t len256 = length >> 3; + + for (int i = 0; i < len256; i += 2) { + // Get rho + simde__m256i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8; + oai_mm256_separate_real_imag_parts(&xmm2, &xmm3, rho01_256i[i], rho01_256i[i + 1]); + + simde__m256i rho_rpi = simde_mm256_adds_epi16(xmm2, xmm3); // rho = Re(rho) + Im(rho) + simde__m256i rho_rmi = simde_mm256_subs_epi16(xmm2, xmm3); // rho* = Re(rho) - Im(rho) + + // Compute the different rhos + simde__m256i rho_rs[32]; + rho_rs[27] = simde_mm256_mulhi_epi16(rho_rpi, ONE_OVER_SQRT_42); + rho_rs[28] = simde_mm256_mulhi_epi16(rho_rmi, ONE_OVER_SQRT_42); + rho_rs[18] = simde_mm256_mulhi_epi16(rho_rpi, THREE_OVER_SQRT_42); + rho_rs[21] = simde_mm256_mulhi_epi16(rho_rmi, THREE_OVER_SQRT_42); + rho_rs[9] = simde_mm256_mulhi_epi16(rho_rpi, FIVE_OVER_SQRT_42); + rho_rs[14] = simde_mm256_mulhi_epi16(rho_rmi, FIVE_OVER_SQRT_42); + rho_rs[0] = simde_mm256_mulhi_epi16(rho_rpi, SEVEN_OVER_SQRT_42); + rho_rs[7] = simde_mm256_mulhi_epi16(rho_rmi, SEVEN_OVER_SQRT_42); + + rho_rs[9] = simde_mm256_slli_epi16(rho_rs[9], 1); + rho_rs[14] = simde_mm256_slli_epi16(rho_rs[14], 1); + rho_rs[0] = simde_mm256_slli_epi16(rho_rs[0], 2); + rho_rs[7] = simde_mm256_slli_epi16(rho_rs[7], 2); + + xmm4 = simde_mm256_mulhi_epi16(xmm2, ONE_OVER_SQRT_42); + xmm5 = simde_mm256_mulhi_epi16(xmm3, ONE_OVER_SQRT_42); + xmm6 = simde_mm256_mulhi_epi16(xmm3, THREE_OVER_SQRT_42); + xmm7 = simde_mm256_mulhi_epi16(xmm3, FIVE_OVER_SQRT_42); + xmm8 = simde_mm256_mulhi_epi16(xmm3, SEVEN_OVER_SQRT_42); + xmm7 = simde_mm256_slli_epi16(xmm7, 1); + xmm8 = simde_mm256_slli_epi16(xmm8, 2); + + rho_rs[26] = simde_mm256_adds_epi16(xmm4, xmm6); + rho_rs[29] = simde_mm256_subs_epi16(xmm4, xmm6); + rho_rs[25] = simde_mm256_adds_epi16(xmm4, xmm7); + rho_rs[30] = simde_mm256_subs_epi16(xmm4, xmm7); + rho_rs[24] = simde_mm256_adds_epi16(xmm4, xmm8); + rho_rs[31] = simde_mm256_subs_epi16(xmm4, xmm8); + + xmm4 = simde_mm256_mulhi_epi16(xmm2, THREE_OVER_SQRT_42); + rho_rs[19] = simde_mm256_adds_epi16(xmm4, xmm5); + rho_rs[20] = simde_mm256_subs_epi16(xmm4, xmm5); + rho_rs[17] = simde_mm256_adds_epi16(xmm4, xmm7); + rho_rs[22] = simde_mm256_subs_epi16(xmm4, xmm7); + rho_rs[16] = simde_mm256_adds_epi16(xmm4, xmm8); + rho_rs[23] = simde_mm256_subs_epi16(xmm4, xmm8); + + xmm4 = simde_mm256_mulhi_epi16(xmm2, FIVE_OVER_SQRT_42); + xmm4 = simde_mm256_slli_epi16(xmm4, 1); + rho_rs[11] = simde_mm256_adds_epi16(xmm4, xmm5); + rho_rs[12] = simde_mm256_subs_epi16(xmm4, xmm5); + rho_rs[10] = simde_mm256_adds_epi16(xmm4, xmm6); + rho_rs[13] = simde_mm256_subs_epi16(xmm4, xmm6); + rho_rs[8] = simde_mm256_adds_epi16(xmm4, xmm8); + rho_rs[15] = simde_mm256_subs_epi16(xmm4, xmm8); + + xmm4 = simde_mm256_mulhi_epi16(xmm2, SEVEN_OVER_SQRT_42); + xmm4 = simde_mm256_slli_epi16(xmm4, 2); + rho_rs[3] = simde_mm256_adds_epi16(xmm4, xmm5); + rho_rs[4] = simde_mm256_subs_epi16(xmm4, xmm5); + rho_rs[2] = simde_mm256_adds_epi16(xmm4, xmm6); + rho_rs[5] = simde_mm256_subs_epi16(xmm4, xmm6); + rho_rs[1] = simde_mm256_adds_epi16(xmm4, xmm7); + rho_rs[6] = simde_mm256_subs_epi16(xmm4, xmm7); + + // Rearrange interfering MF output + simde__m256i y1r, y1i; + oai_mm256_separate_real_imag_parts(&y1r, &y1i, stream1_256i_in[i], stream1_256i_in[i + 1]); + + // Psi_r calculation from rho_rpi or rho_rmi + xmm0 = simde_mm256_broadcastw_epi16(simde_mm_set1_epi16(0)); // ZERO for abs_pi16 + xmm2 = simde_mm256_subs_epi16(rho_rs[0], y1r); + + simde__m256i psi_r_s[64]; + for (int j = 0; j < 32; j++) // psi_r_s[0~31], rho_rs[0~31] + psi_r_s[j] = simde_mm256_abs_epi16(simde_mm256_subs_epi16(rho_rs[j], y1r)); + for (int j = 32; j < 64; j++) // psi_r_s[32~64], rho_rs[31~0] + psi_r_s[j] = simde_mm256_abs_epi16(simde_mm256_adds_epi16(rho_rs[63 - j], y1r)); + + // simde__m256i psi_i calculation from rho_rpi or rho_rmi + simde__m256i psi_i_s[64]; + const uint8_t rho_rs_index[32] = {7, 15, 23, 31, 24, 16, 8, 0, 6, 14, 22, 30, 25, 17, 9, 1, + 5, 13, 21, 29, 26, 18, 10, 2, 4, 12, 20, 28, 27, 19, 11, 3}; + for (int k = 0; k < 32; k += 8) { // psi_i_s[0~31] + for (int j = k; j < k + 4; j++) + psi_i_s[j] = simde_mm256_abs_epi16(simde_mm256_subs_epi16(rho_rs[rho_rs_index[j]], y1i)); + for (int j = k + 4; j < k + 8; j++) + psi_i_s[j] = simde_mm256_abs_epi16(simde_mm256_adds_epi16(rho_rs[rho_rs_index[j]], y1i)); + } + for (int k = 32; k < 64; k += 8) { // psi_i_s[32~64] + for (int j = k; j < k + 4; j++) + psi_i_s[j] = simde_mm256_abs_epi16(simde_mm256_subs_epi16(rho_rs[rho_rs_index[63 - j]], y1i)); + for (int j = k + 4; j < k + 8; j++) + psi_i_s[j] = simde_mm256_abs_epi16(simde_mm256_adds_epi16(rho_rs[rho_rs_index[63 - j]], y1i)); + } + + // Rearrange desired MF output + simde__m256i y0r, y0i; + oai_mm256_separate_real_imag_parts(&y0r, &y0i, stream0_256i_in[i], stream0_256i_in[i + 1]); + + // Rearrange desired channel magnitudes + // [|h|^2(1),|h|^2(1),|h|^2(2),|h|^2(2),...,,|h|^2(7),|h|^2(7)]*(2/sqrt(10)) + // xmm2 is dummy variable that contains the same values as ch_mag_des + oai_mm256_separate_real_imag_parts(&ch_mag_des, &xmm2, ch_mag_256i[i], ch_mag_256i[i + 1]); + + // Rearrange interfering channel magnitudes + oai_mm256_separate_real_imag_parts(&ch_mag_int, &xmm2, ch_mag_256i_i[i], ch_mag_256i_i[i + 1]); + + y0r_one_over_sqrt_21 = simde_mm256_mulhi_epi16(y0r, ONE_OVER_SQRT_42); + y0r_three_over_sqrt_21 = simde_mm256_mulhi_epi16(y0r, THREE_OVER_SQRT_42); + y0r_five_over_sqrt_21 = simde_mm256_mulhi_epi16(y0r, FIVE_OVER_SQRT_42); + y0r_five_over_sqrt_21 = simde_mm256_slli_epi16(y0r_five_over_sqrt_21, 1); + y0r_seven_over_sqrt_21 = simde_mm256_mulhi_epi16(y0r, SEVEN_OVER_SQRT_42); + y0r_seven_over_sqrt_21 = simde_mm256_slli_epi16(y0r_seven_over_sqrt_21, 2); // Q2.14 + + y0i_one_over_sqrt_21 = simde_mm256_mulhi_epi16(y0i, ONE_OVER_SQRT_42); + y0i_three_over_sqrt_21 = simde_mm256_mulhi_epi16(y0i, THREE_OVER_SQRT_42); + y0i_five_over_sqrt_21 = simde_mm256_mulhi_epi16(y0i, FIVE_OVER_SQRT_42); + y0i_five_over_sqrt_21 = simde_mm256_slli_epi16(y0i_five_over_sqrt_21, 1); + y0i_seven_over_sqrt_21 = simde_mm256_mulhi_epi16(y0i, SEVEN_OVER_SQRT_42); + y0i_seven_over_sqrt_21 = simde_mm256_slli_epi16(y0i_seven_over_sqrt_21, 2); // Q2.14 + + simde__m256i y0_s[64]; + const simde__m256i y0r_over_s[8] = {y0r_seven_over_sqrt_21, + y0r_five_over_sqrt_21, + y0r_three_over_sqrt_21, + y0r_one_over_sqrt_21}; + for (int j = 0; j < 32; j += 8) { + y0_s[j + 0] = simde_mm256_adds_epi16(y0r_over_s[j >> 3], y0i_seven_over_sqrt_21); + y0_s[j + 1] = simde_mm256_adds_epi16(y0r_over_s[j >> 3], y0i_five_over_sqrt_21); + y0_s[j + 2] = simde_mm256_adds_epi16(y0r_over_s[j >> 3], y0i_three_over_sqrt_21); + y0_s[j + 3] = simde_mm256_adds_epi16(y0r_over_s[j >> 3], y0i_one_over_sqrt_21); + y0_s[j + 4] = simde_mm256_subs_epi16(y0r_over_s[j >> 3], y0i_one_over_sqrt_21); + y0_s[j + 5] = simde_mm256_subs_epi16(y0r_over_s[j >> 3], y0i_three_over_sqrt_21); + y0_s[j + 6] = simde_mm256_subs_epi16(y0r_over_s[j >> 3], y0i_five_over_sqrt_21); + y0_s[j + 7] = simde_mm256_subs_epi16(y0r_over_s[j >> 3], y0i_seven_over_sqrt_21); + } + + ch_mag_int_with_sigma2 = simde_mm256_srai_epi16(ch_mag_int, 1); // *2 + two_ch_mag_int_with_sigma2 = ch_mag_int; // *4 + three_ch_mag_int_with_sigma2 = simde_mm256_adds_epi16(ch_mag_int_with_sigma2, two_ch_mag_int_with_sigma2); // *6 + simde__m256i a_r_s[64]; + simde__m256i a_i_s[64]; + simde__m256i psi_a_s[64]; + simde__m256i a_sq_s[64]; + for (int j = 0; j < 64; j++) { + // Detection of interference term + a_r_s[j] = interference_abs_64qam_epi16_256(psi_r_s[j], + ch_mag_int_with_sigma2, + two_ch_mag_int_with_sigma2, + three_ch_mag_int_with_sigma2, + ONE_OVER_SQRT_2_42, + THREE_OVER_SQRT_2_42, + FIVE_OVER_SQRT_2_42, + SEVEN_OVER_SQRT_2_42); + a_i_s[j] = interference_abs_64qam_epi16_256(psi_i_s[j], + ch_mag_int_with_sigma2, + two_ch_mag_int_with_sigma2, + three_ch_mag_int_with_sigma2, + ONE_OVER_SQRT_2_42, + THREE_OVER_SQRT_2_42, + FIVE_OVER_SQRT_2_42, + SEVEN_OVER_SQRT_2_42); + + // Calculation of a group of two terms in the bit metric involving product of psi and interference + psi_a_s[j] = prodsum_psi_a_epi16_256(psi_r_s[j], a_r_s[j], psi_i_s[j], a_i_s[j]); + + // Multiply by sqrt(2) + psi_a_s[j] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(psi_a_s[j], ONE_OVER_SQRT_2), 2); + + // Calculation of a group of two terms in the bit metric involving squares of interference + a_sq_s[j] = square_a_64qam_epi16_256(a_r_s[j], a_i_s[j], ch_mag_int, SQRT_42_OVER_FOUR); + } + + // Computing different multiples of ||h0||^2 + simde__m256i ch_mag_with_sigma2[10]; + enum ch_mag_over_42with_sigma2_vals { mag2 = 0, mag10, mag26, mag18, mag34, mag58, mag50, mag74, mag98 }; + // x=1, y=1 + ch_mag_with_sigma2[mag2] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, ONE_OVER_FOUR_SQRT_42), 1); + // x=1, y=3 + ch_mag_with_sigma2[mag10] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, FIVE_OVER_FOUR_SQRT_42), 1); + // x=1, x=5 + ch_mag_with_sigma2[mag26] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, THIRTEEN_OVER_FOUR_SQRT_42), 1); + // x=1, y=7 + ch_mag_with_sigma2[mag50] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, TWENTYFIVE_OVER_FOUR_SQRT_42), 1); + // x=3, y=3 + ch_mag_with_sigma2[mag18] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, NINE_OVER_FOUR_SQRT_42), 1); + // x=3, y=5 + ch_mag_with_sigma2[mag34] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, SEVENTEEN_OVER_FOUR_SQRT_42), 1); + // x=3, y=7 + ch_mag_with_sigma2[mag58] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, TWENTYNINE_OVER_FOUR_SQRT_42), 2); + // x=5, y=5 + ch_mag_with_sigma2[mag50] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, TWENTYFIVE_OVER_FOUR_SQRT_42), 1); + // x=5, y=7 + ch_mag_with_sigma2[mag74] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, THIRTYSEVEN_OVER_FOUR_SQRT_42), 2); + // x=7, y=7 + ch_mag_with_sigma2[mag98] = simde_mm256_slli_epi16(simde_mm256_mulhi_epi16(ch_mag_des, FORTYNINE_OVER_FOUR_SQRT_42), 2); + + // Computing Metrics + simde__m256i bit_met_s[64]; + const enum ch_mag_over_42with_sigma2_vals table[] = { + mag98, mag74, mag58, mag50, mag50, mag58, mag74, mag98, mag74, mag50, mag34, mag26, mag26, mag34, mag50, mag74, + mag58, mag34, mag18, mag10, mag10, mag18, mag34, mag58, mag50, mag26, mag10, mag2, mag2, mag10, mag26, mag50}; + + for (int i = 0; i < 32; i++) { + const simde__m256i x = simde_mm256_adds_epi16(simde_mm256_subs_epi16(psi_a_s[i], a_sq_s[i]), y0_s[i]); + bit_met_s[i] = simde_mm256_subs_epi16(x, ch_mag_with_sigma2[table[i]]); + } + for (int i = 0; i < 32; i++) { + const simde__m256i x = simde_mm256_subs_epi16(simde_mm256_subs_epi16(psi_a_s[32 + i], a_sq_s[32 + i]), y0_s[31 - i]); + bit_met_s[32 + i] = simde_mm256_subs_epi16(x, ch_mag_with_sigma2[table[31 - i]]); + } + + // Detection for bits + simde__m256i logmax_den_re0; + simde__m256i logmax_num_re0; + // Detection for 1st bit + // bit = 1 + xmm0 = max_epi16_256(bit_met_s[56], + bit_met_s[57], + bit_met_s[58], + bit_met_s[59], + bit_met_s[60], + bit_met_s[61], + bit_met_s[62], + bit_met_s[63]); + xmm1 = max_epi16_256(bit_met_s[48], + bit_met_s[49], + bit_met_s[50], + bit_met_s[51], + bit_met_s[52], + bit_met_s[53], + bit_met_s[54], + bit_met_s[55]); + xmm2 = max_epi16_256(bit_met_s[40], + bit_met_s[41], + bit_met_s[42], + bit_met_s[43], + bit_met_s[44], + bit_met_s[45], + bit_met_s[46], + bit_met_s[47]); + xmm3 = max_epi16_256(bit_met_s[32], + bit_met_s[33], + bit_met_s[34], + bit_met_s[35], + bit_met_s[36], + bit_met_s[37], + bit_met_s[38], + bit_met_s[39]); + logmax_den_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); + + // bit = 0 + xmm0 = max_epi16_256(bit_met_s[0], + bit_met_s[1], + bit_met_s[2], + bit_met_s[3], + bit_met_s[4], + bit_met_s[5], + bit_met_s[6], + bit_met_s[7]); + xmm1 = max_epi16_256(bit_met_s[8], + bit_met_s[9], + bit_met_s[10], + bit_met_s[11], + bit_met_s[12], + bit_met_s[13], + bit_met_s[14], + bit_met_s[15]); + xmm2 = max_epi16_256(bit_met_s[16], + bit_met_s[17], + bit_met_s[18], + bit_met_s[19], + bit_met_s[20], + bit_met_s[21], + bit_met_s[22], + bit_met_s[23]); + xmm3 = max_epi16_256(bit_met_s[24], + bit_met_s[25], + bit_met_s[26], + bit_met_s[27], + bit_met_s[28], + bit_met_s[29], + bit_met_s[30], + bit_met_s[31]); + logmax_num_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); + + y0r = simde_mm256_subs_epi16(logmax_num_re0, logmax_den_re0); + + // Detection for 2nd bit + // bit = 1 + xmm0 = max_epi16_256(bit_met_s[4], + bit_met_s[12], + bit_met_s[20], + bit_met_s[28], + bit_met_s[36], + bit_met_s[44], + bit_met_s[52], + bit_met_s[60]); + xmm1 = max_epi16_256(bit_met_s[5], + bit_met_s[13], + bit_met_s[21], + bit_met_s[29], + bit_met_s[37], + bit_met_s[45], + bit_met_s[53], + bit_met_s[61]); + xmm2 = max_epi16_256(bit_met_s[6], + bit_met_s[14], + bit_met_s[22], + bit_met_s[30], + bit_met_s[38], + bit_met_s[46], + bit_met_s[54], + bit_met_s[62]); + xmm3 = max_epi16_256(bit_met_s[7], + bit_met_s[15], + bit_met_s[23], + bit_met_s[31], + bit_met_s[39], + bit_met_s[47], + bit_met_s[55], + bit_met_s[63]); + logmax_den_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); + + // bit = 0 + xmm0 = max_epi16_256(bit_met_s[3], + bit_met_s[11], + bit_met_s[19], + bit_met_s[27], + bit_met_s[35], + bit_met_s[43], + bit_met_s[51], + bit_met_s[59]); + xmm1 = max_epi16_256(bit_met_s[2], + bit_met_s[10], + bit_met_s[18], + bit_met_s[26], + bit_met_s[34], + bit_met_s[42], + bit_met_s[50], + bit_met_s[58]); + xmm2 = max_epi16_256(bit_met_s[1], + bit_met_s[9], + bit_met_s[17], + bit_met_s[25], + bit_met_s[33], + bit_met_s[41], + bit_met_s[49], + bit_met_s[57]); + xmm3 = max_epi16_256(bit_met_s[0], + bit_met_s[8], + bit_met_s[16], + bit_met_s[24], + bit_met_s[32], + bit_met_s[40], + bit_met_s[48], + bit_met_s[56]); + logmax_num_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); + + y1r = simde_mm256_subs_epi16(logmax_num_re0, logmax_den_re0); + + // Detection for 3rd bit + xmm0 = max_epi16_256(bit_met_s[63], + bit_met_s[62], + bit_met_s[61], + bit_met_s[60], + bit_met_s[59], + bit_met_s[58], + bit_met_s[57], + bit_met_s[56]); + xmm1 = max_epi16_256(bit_met_s[55], + bit_met_s[54], + bit_met_s[53], + bit_met_s[52], + bit_met_s[51], + bit_met_s[50], + bit_met_s[49], + bit_met_s[48]); + xmm2 = max_epi16_256(bit_met_s[15], + bit_met_s[14], + bit_met_s[13], + bit_met_s[12], + bit_met_s[11], + bit_met_s[10], + bit_met_s[9], + bit_met_s[8]); + xmm3 = max_epi16_256(bit_met_s[7], + bit_met_s[6], + bit_met_s[5], + bit_met_s[4], + bit_met_s[3], + bit_met_s[2], + bit_met_s[1], + bit_met_s[0]); + logmax_den_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); + + xmm0 = max_epi16_256(bit_met_s[47], + bit_met_s[46], + bit_met_s[45], + bit_met_s[44], + bit_met_s[43], + bit_met_s[42], + bit_met_s[41], + bit_met_s[40]); + xmm1 = max_epi16_256(bit_met_s[39], + bit_met_s[38], + bit_met_s[37], + bit_met_s[36], + bit_met_s[35], + bit_met_s[34], + bit_met_s[33], + bit_met_s[32]); + xmm2 = max_epi16_256(bit_met_s[31], + bit_met_s[30], + bit_met_s[29], + bit_met_s[28], + bit_met_s[27], + bit_met_s[26], + bit_met_s[25], + bit_met_s[24]); + xmm3 = max_epi16_256(bit_met_s[23], + bit_met_s[22], + bit_met_s[21], + bit_met_s[20], + bit_met_s[19], + bit_met_s[18], + bit_met_s[17], + bit_met_s[16]); + logmax_num_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); + + simde__m256i y2r = simde_mm256_subs_epi16(logmax_num_re0, logmax_den_re0); + + // Detection for 4th bit + xmm0 = max_epi16_256(bit_met_s[0], + bit_met_s[8], + bit_met_s[16], + bit_met_s[24], + bit_met_s[32], + bit_met_s[40], + bit_met_s[48], + bit_met_s[56]); + xmm1 = max_epi16_256(bit_met_s[1], + bit_met_s[9], + bit_met_s[17], + bit_met_s[25], + bit_met_s[33], + bit_met_s[41], + bit_met_s[49], + bit_met_s[57]); + xmm2 = max_epi16_256(bit_met_s[6], + bit_met_s[14], + bit_met_s[22], + bit_met_s[30], + bit_met_s[38], + bit_met_s[46], + bit_met_s[54], + bit_met_s[62]); + xmm3 = max_epi16_256(bit_met_s[7], + bit_met_s[15], + bit_met_s[23], + bit_met_s[31], + bit_met_s[39], + bit_met_s[47], + bit_met_s[55], + bit_met_s[63]); + logmax_den_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); + + xmm0 = max_epi16_256(bit_met_s[4], + bit_met_s[12], + bit_met_s[20], + bit_met_s[28], + bit_met_s[36], + bit_met_s[44], + bit_met_s[52], + bit_met_s[60]); + xmm1 = max_epi16_256(bit_met_s[5], + bit_met_s[13], + bit_met_s[21], + bit_met_s[29], + bit_met_s[37], + bit_met_s[45], + bit_met_s[53], + bit_met_s[61]); + xmm2 = max_epi16_256(bit_met_s[3], + bit_met_s[11], + bit_met_s[19], + bit_met_s[27], + bit_met_s[35], + bit_met_s[43], + bit_met_s[51], + bit_met_s[59]); + xmm3 = max_epi16_256(bit_met_s[2], + bit_met_s[10], + bit_met_s[18], + bit_met_s[26], + bit_met_s[34], + bit_met_s[42], + bit_met_s[50], + bit_met_s[58]); + logmax_num_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); + + y0i = simde_mm256_subs_epi16(logmax_num_re0, logmax_den_re0); + + // Detection for 5th bit + xmm0 = max_epi16_256(bit_met_s[63], + bit_met_s[62], + bit_met_s[61], + bit_met_s[60], + bit_met_s[59], + bit_met_s[58], + bit_met_s[57], + bit_met_s[56]); + xmm1 = max_epi16_256(bit_met_s[39], + bit_met_s[38], + bit_met_s[37], + bit_met_s[36], + bit_met_s[35], + bit_met_s[34], + bit_met_s[33], + bit_met_s[32]); + xmm2 = max_epi16_256(bit_met_s[31], + bit_met_s[30], + bit_met_s[29], + bit_met_s[28], + bit_met_s[27], + bit_met_s[26], + bit_met_s[25], + bit_met_s[24]); + xmm3 = max_epi16_256(bit_met_s[7], + bit_met_s[6], + bit_met_s[5], + bit_met_s[4], + bit_met_s[3], + bit_met_s[2], + bit_met_s[1], + bit_met_s[0]); + logmax_den_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); + + xmm0 = max_epi16_256(bit_met_s[55], + bit_met_s[54], + bit_met_s[53], + bit_met_s[52], + bit_met_s[51], + bit_met_s[50], + bit_met_s[49], + bit_met_s[48]); + xmm1 = max_epi16_256(bit_met_s[47], + bit_met_s[46], + bit_met_s[45], + bit_met_s[44], + bit_met_s[43], + bit_met_s[42], + bit_met_s[41], + bit_met_s[40]); + xmm2 = max_epi16_256(bit_met_s[23], + bit_met_s[22], + bit_met_s[21], + bit_met_s[20], + bit_met_s[19], + bit_met_s[18], + bit_met_s[17], + bit_met_s[16]); + xmm3 = max_epi16_256(bit_met_s[15], + bit_met_s[14], + bit_met_s[13], + bit_met_s[12], + bit_met_s[11], + bit_met_s[10], + bit_met_s[9], + bit_met_s[8]); + logmax_num_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); + + y1i = simde_mm256_subs_epi16(logmax_num_re0, logmax_den_re0); + + // Detection for 6th bit + xmm0 = max_epi16_256(bit_met_s[0], + bit_met_s[8], + bit_met_s[16], + bit_met_s[24], + bit_met_s[32], + bit_met_s[40], + bit_met_s[48], + bit_met_s[56]); + xmm1 = max_epi16_256(bit_met_s[3], + bit_met_s[11], + bit_met_s[19], + bit_met_s[27], + bit_met_s[35], + bit_met_s[43], + bit_met_s[51], + bit_met_s[59]); + xmm2 = max_epi16_256(bit_met_s[4], + bit_met_s[12], + bit_met_s[20], + bit_met_s[28], + bit_met_s[36], + bit_met_s[44], + bit_met_s[52], + bit_met_s[60]); + xmm3 = max_epi16_256(bit_met_s[7], + bit_met_s[15], + bit_met_s[23], + bit_met_s[31], + bit_met_s[39], + bit_met_s[47], + bit_met_s[55], + bit_met_s[63]); + logmax_den_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); + + xmm0 = max_epi16_256(bit_met_s[6], + bit_met_s[14], + bit_met_s[22], + bit_met_s[30], + bit_met_s[38], + bit_met_s[46], + bit_met_s[54], + bit_met_s[62]); + xmm1 = max_epi16_256(bit_met_s[5], + bit_met_s[13], + bit_met_s[21], + bit_met_s[29], + bit_met_s[37], + bit_met_s[45], + bit_met_s[53], + bit_met_s[61]); + xmm2 = max_epi16_256(bit_met_s[2], + bit_met_s[10], + bit_met_s[18], + bit_met_s[26], + bit_met_s[34], + bit_met_s[42], + bit_met_s[50], + bit_met_s[58]); + xmm3 = max_epi16_256(bit_met_s[1], + bit_met_s[9], + bit_met_s[17], + bit_met_s[25], + bit_met_s[33], + bit_met_s[41], + bit_met_s[49], + bit_met_s[57]); + logmax_num_re0 = simde_mm256_max_epi16(simde_mm256_max_epi16(xmm0, xmm1), simde_mm256_max_epi16(xmm2, xmm3)); + + simde__m256i y2i = simde_mm256_subs_epi16(logmax_num_re0, logmax_den_re0); + + // Map to output stream, difficult to do in SIMD since we have 6 16bit LLRs + for (int re = 0; re < 16; re++) { + *stream0_out++ = ((short *)&y0r)[re]; + *stream0_out++ = ((short *)&y1r)[re]; + *stream0_out++ = ((short *)&y2r)[re]; + *stream0_out++ = ((short *)&y0i)[re]; + *stream0_out++ = ((short *)&y1i)[re]; + *stream0_out++ = ((short *)&y2i)[re]; + } + } +#endif +} + +static void nr_ml_llr_shift(int16_t *llr_layer0, int16_t *llr_layer1, uint32_t nb_re, int shift) +{ + simde__m128i *llr_layers0 = (simde__m128i *)llr_layer0; + simde__m128i *llr_layers1 = (simde__m128i *)llr_layer1; + + uint8_t mem_offset = ((16 - ((long)llr_layers0)) & 0xF) >> 2; + + if (mem_offset > 0) { + c16_t *llr_layers0_c16 = (c16_t *)llr_layer0; + c16_t *llr_layers1_c16 = (c16_t *)llr_layer1; + for (int i = 0; i < mem_offset; i++) { + llr_layers0_c16[i] = c16Shift(llr_layers0_c16[i], shift); + llr_layers1_c16[i] = c16Shift(llr_layers1_c16[i], shift); + } + llr_layers0 = (simde__m128i *)&llr_layer0[mem_offset * 2]; + llr_layers1 = (simde__m128i *)&llr_layer1[mem_offset * 2]; + } + + for (int i = 0; i < nb_re >> 2; i++) { + llr_layers0[i] = simde_mm_srai_epi16(llr_layers0[i], shift); + llr_layers1[i] = simde_mm_srai_epi16(llr_layers1[i], shift); + } +} + +void nr_compute_ML_llr(c16_t *rxdataF_comp0, + c16_t *rxdataF_comp1, + c16_t *ch_mag0, + c16_t *ch_mag1, + int16_t *llr_layers0, + int16_t *llr_layers1, + c16_t *rho0, + c16_t *rho1, + uint32_t nb_re, + uint8_t mod_order) +{ + switch (mod_order) { + case 2: + nr_qpsk_llr_2layer(rxdataF_comp0, rxdataF_comp1, llr_layers0, rho0, nb_re); + nr_qpsk_llr_2layer(rxdataF_comp1, rxdataF_comp0, llr_layers1, rho1, nb_re); + nr_ml_llr_shift((int16_t *)llr_layers0, (int16_t *)llr_layers1, nb_re, 4); + break; + case 4: + nr_qam16_llr_2layer(rxdataF_comp0, rxdataF_comp1, ch_mag0, ch_mag1, llr_layers0, rho0, nb_re); + nr_qam16_llr_2layer(rxdataF_comp1, rxdataF_comp0, ch_mag1, ch_mag0, llr_layers1, rho1, nb_re); + break; + case 6: + nr_qam64_llr_2layer(rxdataF_comp0, rxdataF_comp1, ch_mag0, ch_mag1, llr_layers0, rho0, nb_re); + nr_qam64_llr_2layer(rxdataF_comp1, rxdataF_comp0, ch_mag1, ch_mag0, llr_layers1, rho1, nb_re); + break; + default: + AssertFatal(1 == 0, "nr_compute_ML_llr: invalid mod_order, Qm = %d\n", mod_order); + } +} +// Zero Forcing Rx function: nr_det_HhH() +static void nr_det_HhH(c16_t *after_mf_00, // a + c16_t *after_mf_01, // b + c16_t *after_mf_10, // c + c16_t *after_mf_11, // d + uint32_t *det_fin, // 1/ad-bc + unsigned short nb_rb) +{ + simde__m128i *after_mf_00_128, *after_mf_01_128, *after_mf_10_128, *after_mf_11_128, ad_re_128, bc_re_128; // ad_im_128, + // bc_im_128; + simde__m128i *det_fin_128, det_re_128; // det_im_128, tmp_det0, tmp_det1; + + after_mf_00_128 = (simde__m128i *)after_mf_00; + after_mf_01_128 = (simde__m128i *)after_mf_01; + after_mf_10_128 = (simde__m128i *)after_mf_10; + after_mf_11_128 = (simde__m128i *)after_mf_11; + + det_fin_128 = (simde__m128i *)det_fin; + + for (unsigned short rb = 0; rb < 3 * nb_rb; rb++) { + // complex multiplication (I_a+jQ_a)(I_d+jQ_d) = (I_aI_d - Q_aQ_d) + j(Q_aI_d + I_aQ_d) + // The imag part is often zero, we compute only the real part + ad_re_128 = simde_mm_madd_epi16(oai_mm_conj(after_mf_00_128[0]), after_mf_11_128[0]); // Re: I_a0*I_d0 - Q_a1*Q_d1 + // ad_im_128 = simde_mm_madd_epi16(oai_mm_swap(after_mf_00_128[0]),after_mf_11_128[0]);//Im: (Q_aI_d + I_aQ_d) + + // complex multiplication (I_b+jQ_b)(I_c+jQ_c) = (I_bI_c - Q_bQ_c) + j(Q_bI_c + I_bQ_c) + // The imag part is often zero, we compute only the real part + bc_re_128 = simde_mm_madd_epi16(oai_mm_conj(after_mf_01_128[0]), after_mf_10_128[0]); // Re: I_b0*I_c0 - Q_b1*Q_c1 + // bc_im_128 = simde_mm_madd_epi16(oai_mm_swap(after_mf_01_128[0]),after_mf_10_128[0]);//Im: (Q_bI_c + I_bQ_c) + + det_re_128 = simde_mm_sub_epi32(ad_re_128, bc_re_128); + // det_im_128 = simde_mm_sub_epi32(ad_im_128, bc_im_128); + + // det in Q30 format + det_fin_128[0] = simde_mm_abs_epi32(det_re_128); + +#ifdef DEBUG_DLSCH_DEMOD + printf("\n Computing det_HhH_inv \n"); + // print_ints("det_re_128:",(int32_t*)&det_re_128); + // print_ints("det_im_128:",(int32_t*)&det_im_128); + print_ints("det_fin_128:", (int32_t *)&det_fin_128[0]); +#endif + det_fin_128 += 1; + after_mf_00_128 += 1; + after_mf_01_128 += 1; + after_mf_10_128 += 1; + after_mf_11_128 += 1; + } +} + +/* Zero Forcing Rx function: nr_conjch0_mult_ch1() + * + * + * */ +// TODO: This function is just a wrapper, can be removed. +static void nr_conjch0_mult_ch1(c16_t *ch0, c16_t *ch1, c16_t *ch0conj_ch1, unsigned short nb_rb, unsigned char output_shift0) +{ + // This function is used to compute multiplications in H_hermitian * H matrix + mult_cpx_conj_vector(ch0, ch1, ch0conj_ch1, 12 * nb_rb, output_shift0); +} + +static simde__m128i nr_comp_muli_sum(simde__m128i input_x, + simde__m128i input_y, + simde__m128i input_w, + simde__m128i input_z, + simde__m128i det) +{ + // complex multiplication (x_re + jx_im)*(y_re + jy_im) = (x_re*y_re - x_im*y_im) + j(x_im*y_re + x_re*y_im) + // complex multiplication (w_re + jw_im)*(z_re + jz_im) = (w_re*z_re - w_im*z_im) + j(w_im*z_re + w_re*z_im) + // the real part + simde__m128i xy_re_128 = simde_mm_madd_epi16(oai_mm_conj(input_x), input_y); // Re: (x_re*y_re - x_im*y_im) + simde__m128i wz_re_128 = simde_mm_madd_epi16(oai_mm_conj(input_w), input_z); // Re: (w_re*z_re - w_im*z_im) + xy_re_128 = simde_mm_sub_epi32(xy_re_128, wz_re_128); + + // the imag part + simde__m128i xy_im_128 = simde_mm_madd_epi16(oai_mm_swap(input_x), input_y); // Im: (x_im*y_re + x_re*y_im) + simde__m128i wz_im_128 = simde_mm_madd_epi16(oai_mm_swap(input_w), input_z); // Im: (w_im*z_re + w_re*z_im) + xy_im_128 = simde_mm_sub_epi32(xy_im_128, wz_im_128); + + // print_ints("rx_re:",(int32_t*)&xy_re_128[0]); + // print_ints("rx_Img:",(int32_t*)&xy_im_128[0]); + // divide by matrix det and convert back to Q15 before packing + uint64_t sum_det = 0; + for (int k = 0; k < 4; k++) { + sum_det += (((uint32_t *)&det)[k]); + } + // Add bias to reduce rounding error + sum_det = (sum_det + 2) >> 2; + + int b = log2_approx(sum_det) - 8; + if (b > 0) { + xy_re_128 = simde_mm_srai_epi32(xy_re_128, b); + xy_im_128 = simde_mm_srai_epi32(xy_im_128, b); + } else { + xy_re_128 = simde_mm_slli_epi32(xy_re_128, -b); + xy_im_128 = simde_mm_slli_epi32(xy_im_128, -b); + } + + simde__m128i output = oai_mm_pack(xy_re_128, xy_im_128); + + return (output); +} + +/* Zero Forcing Rx function: nr_construct_HhH_elements() + * + * + * */ +static void nr_construct_HhH_elements(c16_t *conjch00_ch00, + c16_t *conjch01_ch01, + c16_t *conjch11_ch11, + c16_t *conjch10_ch10, // + c16_t *conjch20_ch20, + c16_t *conjch21_ch21, + c16_t *conjch30_ch30, + c16_t *conjch31_ch31, + c16_t *conjch00_ch01, // 00_01 + c16_t *conjch01_ch00, // 01_00 + c16_t *conjch10_ch11, // 10_11 + c16_t *conjch11_ch10, // 11_10 + c16_t *conjch20_ch21, + c16_t *conjch21_ch20, + c16_t *conjch30_ch31, + c16_t *conjch31_ch30, + c16_t *after_mf_00, + c16_t *after_mf_01, + c16_t *after_mf_10, + c16_t *after_mf_11, + unsigned short nb_rb) +{ + // This function is used to construct the (H_hermitian * H matrix) matrix elements + simde__m128i *conjch00_ch00_128 = (simde__m128i *)conjch00_ch00; + simde__m128i *conjch01_ch01_128 = (simde__m128i *)conjch01_ch01; + simde__m128i *conjch11_ch11_128 = (simde__m128i *)conjch11_ch11; + simde__m128i *conjch10_ch10_128 = (simde__m128i *)conjch10_ch10; + + simde__m128i *conjch20_ch20_128 = (simde__m128i *)conjch20_ch20; + simde__m128i *conjch21_ch21_128 = (simde__m128i *)conjch21_ch21; + simde__m128i *conjch30_ch30_128 = (simde__m128i *)conjch30_ch30; + simde__m128i *conjch31_ch31_128 = (simde__m128i *)conjch31_ch31; + + simde__m128i *conjch00_ch01_128 = (simde__m128i *)conjch00_ch01; + simde__m128i *conjch01_ch00_128 = (simde__m128i *)conjch01_ch00; + simde__m128i *conjch10_ch11_128 = (simde__m128i *)conjch10_ch11; + simde__m128i *conjch11_ch10_128 = (simde__m128i *)conjch11_ch10; + + simde__m128i *conjch20_ch21_128 = (simde__m128i *)conjch20_ch21; + simde__m128i *conjch21_ch20_128 = (simde__m128i *)conjch21_ch20; + simde__m128i *conjch30_ch31_128 = (simde__m128i *)conjch30_ch31; + simde__m128i *conjch31_ch30_128 = (simde__m128i *)conjch31_ch30; + + simde__m128i *after_mf_00_128 = (simde__m128i *)after_mf_00; + simde__m128i *after_mf_01_128 = (simde__m128i *)after_mf_01; + simde__m128i *after_mf_10_128 = (simde__m128i *)after_mf_10; + simde__m128i *after_mf_11_128 = (simde__m128i *)after_mf_11; + + for (unsigned short rb = 0; rb < 3 * nb_rb; rb++) { + after_mf_00_128[0] = simde_mm_adds_epi16(conjch00_ch00_128[0], conjch10_ch10_128[0]); // 00_00 + 10_10 + if (conjch20_ch20 != NULL) + after_mf_00_128[0] = simde_mm_adds_epi16(after_mf_00_128[0], conjch20_ch20_128[0]); + if (conjch30_ch30 != NULL) + after_mf_00_128[0] = simde_mm_adds_epi16(after_mf_00_128[0], conjch30_ch30_128[0]); + + after_mf_11_128[0] = simde_mm_adds_epi16(conjch01_ch01_128[0], conjch11_ch11_128[0]); // 01_01 + 11_11 + if (conjch21_ch21 != NULL) + after_mf_11_128[0] = simde_mm_adds_epi16(after_mf_11_128[0], conjch21_ch21_128[0]); + if (conjch31_ch31 != NULL) + after_mf_11_128[0] = simde_mm_adds_epi16(after_mf_11_128[0], conjch31_ch31_128[0]); + + after_mf_01_128[0] = simde_mm_adds_epi16(conjch00_ch01_128[0], conjch10_ch11_128[0]); // 00_01 + 10_11 + if (conjch20_ch21 != NULL) + after_mf_01_128[0] = simde_mm_adds_epi16(after_mf_01_128[0], conjch20_ch21_128[0]); + if (conjch30_ch31 != NULL) + after_mf_01_128[0] = simde_mm_adds_epi16(after_mf_01_128[0], conjch30_ch31_128[0]); + + after_mf_10_128[0] = simde_mm_adds_epi16(conjch01_ch00_128[0], conjch11_ch10_128[0]); // 01_00 + 11_10 + if (conjch21_ch20 != NULL) + after_mf_10_128[0] = simde_mm_adds_epi16(after_mf_10_128[0], conjch21_ch20_128[0]); + if (conjch31_ch30 != NULL) + after_mf_10_128[0] = simde_mm_adds_epi16(after_mf_10_128[0], conjch31_ch30_128[0]); + +#ifdef DEBUG_DLSCH_DEMOD + if ((rb <= 30)) { + printf(" \n construct_HhH_elements \n"); + print_shorts("after_mf_00_128:", (int16_t *)&after_mf_00_128[0]); + print_shorts("after_mf_01_128:", (int16_t *)&after_mf_01_128[0]); + print_shorts("after_mf_10_128:", (int16_t *)&after_mf_10_128[0]); + print_shorts("after_mf_11_128:", (int16_t *)&after_mf_11_128[0]); + } +#endif + conjch00_ch00_128 += 1; + conjch10_ch10_128 += 1; + conjch01_ch01_128 += 1; + conjch11_ch11_128 += 1; + + if (conjch20_ch20 != NULL) + conjch20_ch20_128 += 1; + if (conjch21_ch21 != NULL) + conjch21_ch21_128 += 1; + if (conjch30_ch30 != NULL) + conjch30_ch30_128 += 1; + if (conjch31_ch31 != NULL) + conjch31_ch31_128 += 1; + + conjch00_ch01_128 += 1; + conjch01_ch00_128 += 1; + conjch10_ch11_128 += 1; + conjch11_ch10_128 += 1; + + if (conjch20_ch21 != NULL) + conjch20_ch21_128 += 1; + if (conjch21_ch20 != NULL) + conjch21_ch20_128 += 1; + if (conjch30_ch31 != NULL) + conjch30_ch31_128 += 1; + if (conjch31_ch30 != NULL) + conjch31_ch30_128 += 1; + + after_mf_00_128 += 1; + after_mf_01_128 += 1; + after_mf_10_128 += 1; + after_mf_11_128 += 1; + } +} + +// MMSE Rx function: nr_mmse_2layers() +uint8_t nr_mmse_2layers(c16_t **rxdataF_comp, + uint32_t buffer_length, + int nb_rx_ant, + int nb_layers, + c16_t ch_mag[nb_layers][buffer_length], + c16_t ch_magb[nb_layers][buffer_length], + c16_t ch_magc[nb_layers][buffer_length], + c16_t ch_estimates_ext[][nb_rx_ant][buffer_length], + unsigned short nb_rb, + unsigned char mod_order, + int shift, + unsigned char symbol, + int length, + uint32_t noise_var) +{ + uint32_t nb_rb_0 = length / 12 + ((length % 12) ? 1 : 0); + + /* we need at least alignment to 16 bytes, let's put 32 to be sure + * (maybe not necessary but doesn't hurt) + */ + c16_t conjch00_ch01[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch01_ch00[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch10_ch11[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch11_ch10[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch00_ch00[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch01_ch01[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch10_ch10[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch11_ch11[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch20_ch20[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch21_ch21[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch30_ch30[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch31_ch31[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch20_ch21[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch30_ch31[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch21_ch20[12 * nb_rb] __attribute__((aligned(32))); + c16_t conjch31_ch30[12 * nb_rb] __attribute__((aligned(32))); + + c16_t af_mf_00[12 * nb_rb] __attribute__((aligned(32))); + c16_t af_mf_01[12 * nb_rb] __attribute__((aligned(32))); + c16_t af_mf_10[12 * nb_rb] __attribute__((aligned(32))); + c16_t af_mf_11[12 * nb_rb] __attribute__((aligned(32))); + uint32_t determ_fin[12 * nb_rb] __attribute__((aligned(32))); + + c16_t *ch00, *ch01, *ch10, *ch11; + c16_t *ch20, *ch30, *ch21, *ch31; + switch (nb_rx_ant) { + case 2: // + ch00 = ch_estimates_ext[0][0]; + ch01 = ch_estimates_ext[1][0]; + ch10 = ch_estimates_ext[0][1]; + ch11 = ch_estimates_ext[1][1]; + ch20 = NULL; + ch21 = NULL; + ch30 = NULL; + ch31 = NULL; + break; + + case 4: // + ch00 = ch_estimates_ext[0][0]; + ch01 = ch_estimates_ext[1][0]; + ch10 = ch_estimates_ext[0][1]; + ch11 = ch_estimates_ext[1][1]; + ch20 = ch_estimates_ext[0][2]; + ch21 = ch_estimates_ext[1][2]; + ch30 = ch_estimates_ext[0][3]; + ch31 = ch_estimates_ext[1][3]; + break; + + default: + return -1; + break; + } + + /* 1- Compute the rx channel matrix after compensation: (1/2^log2_max)x(H_herm x H) + * for n_rx = 2 + * |conj_H_00 conj_H_10| | H_00 H_01| |(conj_H_00xH_00+conj_H_10xH_10) (conj_H_00xH_01+conj_H_10xH_11)| + * | | x | | = | | + * |conj_H_01 conj_H_11| | H_10 H_11| |(conj_H_01xH_00+conj_H_11xH_10) (conj_H_01xH_01+conj_H_11xH_11)| + * + */ + + if (nb_rx_ant >= 2) { + // (1/2^log2_maxh)*conj_H_00xH_00: (1/(64*2))conjH_00*H_00*2^15 + nr_conjch0_mult_ch1(ch00, ch00, conjch00_ch00, nb_rb_0, shift); + // (1/2^log2_maxh)*conj_H_10xH_10: (1/(64*2))conjH_10*H_10*2^15 + nr_conjch0_mult_ch1(ch10, ch10, conjch10_ch10, nb_rb_0, shift); + // conj_H_00xH_01 + nr_conjch0_mult_ch1(ch00, ch01, conjch00_ch01, nb_rb_0, + shift); // this shift is equal to the channel level log2_maxh + // conj_H_10xH_11 + nr_conjch0_mult_ch1(ch10, ch11, conjch10_ch11, nb_rb_0, shift); + // conj_H_01xH_01 + nr_conjch0_mult_ch1(ch01, ch01, conjch01_ch01, nb_rb_0, shift); + // conj_H_11xH_11 + nr_conjch0_mult_ch1(ch11, ch11, conjch11_ch11, nb_rb_0, shift); + // conj_H_01xH_00 + nr_conjch0_mult_ch1(ch01, ch00, conjch01_ch00, nb_rb_0, shift); + // conj_H_11xH_10 + nr_conjch0_mult_ch1(ch11, ch10, conjch11_ch10, nb_rb_0, shift); + } + if (nb_rx_ant == 4) { + // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2*16))conjH_20*H_20*2^15 + nr_conjch0_mult_ch1(ch20, ch20, conjch20_ch20, nb_rb_0, shift); + + // (1/2^log2_maxh)*conj_H_30xH_30: (1/(64*2*4))conjH_30*H_30*2^15 + nr_conjch0_mult_ch1(ch30, ch30, conjch30_ch30, nb_rb_0, shift); + + // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2))conjH_20*H_20*2^15 + nr_conjch0_mult_ch1(ch20, ch21, conjch20_ch21, nb_rb_0, shift); + + nr_conjch0_mult_ch1(ch30, ch31, conjch30_ch31, nb_rb_0, shift); + + nr_conjch0_mult_ch1(ch21, ch21, conjch21_ch21, nb_rb_0, shift); + + nr_conjch0_mult_ch1(ch31, ch31, conjch31_ch31, nb_rb_0, shift); + + // (1/2^log2_maxh)*conj_H_20xH_20: (1/(64*2))conjH_20*H_20*2^15 + nr_conjch0_mult_ch1(ch21, ch20, conjch21_ch20, nb_rb_0, shift); + + nr_conjch0_mult_ch1(ch31, ch30, conjch31_ch30, nb_rb_0, shift); + + nr_construct_HhH_elements(conjch00_ch00, + conjch01_ch01, + conjch11_ch11, + conjch10_ch10, // + conjch20_ch20, + conjch21_ch21, + conjch30_ch30, + conjch31_ch31, + conjch00_ch01, + conjch01_ch00, + conjch10_ch11, + conjch11_ch10, // + conjch20_ch21, + conjch21_ch20, + conjch30_ch31, + conjch31_ch30, + af_mf_00, + af_mf_01, + af_mf_10, + af_mf_11, + nb_rb_0); + } + if (nb_rx_ant == 2) { + nr_construct_HhH_elements(conjch00_ch00, + conjch01_ch01, + conjch11_ch11, + conjch10_ch10, // + NULL, + NULL, + NULL, + NULL, + conjch00_ch01, + conjch01_ch00, + conjch10_ch11, + conjch11_ch10, // + NULL, + NULL, + NULL, + NULL, + af_mf_00, + af_mf_01, + af_mf_10, + af_mf_11, + nb_rb_0); + } + + // Add noise_var such that: H^h * H + noise_var * I + if (noise_var != 0) { + simde__m128i nvar_128i = simde_mm_set1_epi32(noise_var); + simde__m128i *af_mf_00_128i = (simde__m128i *)af_mf_00; + simde__m128i *af_mf_11_128i = (simde__m128i *)af_mf_11; + for (int k = 0; k < 3 * nb_rb_0; k++) { + af_mf_00_128i[0] = simde_mm_add_epi32(af_mf_00_128i[0], nvar_128i); + af_mf_11_128i[0] = simde_mm_add_epi32(af_mf_11_128i[0], nvar_128i); + af_mf_00_128i++; + af_mf_11_128i++; + } + } + + // det_HhH = ad -bc + nr_det_HhH(af_mf_00, // a + af_mf_01, // b + af_mf_10, // c + af_mf_11, // d + determ_fin, + nb_rb_0); + /* 2- Compute the channel matrix inversion ********************************** + * + * |(conj_H_00xH_00+conj_H_10xH_10) (conj_H_00xH_01+conj_H_10xH_11)| + * A= | | + * |(conj_H_01xH_00+conj_H_11xH_10) (conj_H_01xH_01+conj_H_11xH_11)| + * + * + * + *inv(A) =(1/det)*[d -b + * -c a] + * + * + **************************************************************************/ + simde__m128i *ch_mag128_0 = NULL, *ch_mag128b_0 = NULL, *ch_mag128c_0 = NULL; // Layer 0 + simde__m128i *ch_mag128_1 = NULL, *ch_mag128b_1 = NULL, *ch_mag128c_1 = NULL; // Layer 1 + simde__m128i mmtmpD0, mmtmpD1, mmtmpD2, mmtmpD3; + simde__m128i QAM_amp128 = {0}, QAM_amp128b = {0}, QAM_amp128c = {0}; + + simde__m128i *determ_fin_128 = (simde__m128i *)&determ_fin[0]; + + simde__m128i *after_mf_a_128 = (simde__m128i *)af_mf_00; + simde__m128i *after_mf_b_128 = (simde__m128i *)af_mf_01; + simde__m128i *after_mf_c_128 = (simde__m128i *)af_mf_10; + simde__m128i *after_mf_d_128 = (simde__m128i *)af_mf_11; + + simde__m128i *rxdataF_comp128_0 = (simde__m128i *)&rxdataF_comp[0][symbol * buffer_length]; + simde__m128i *rxdataF_comp128_1 = (simde__m128i *)&rxdataF_comp[nb_rx_ant][symbol * buffer_length]; + + if (mod_order > 2) { + if (mod_order == 4) { + QAM_amp128 = simde_mm_set1_epi16(QAM16_n1); // 2/sqrt(10) + QAM_amp128b = simde_mm_setzero_si128(); + QAM_amp128c = simde_mm_setzero_si128(); + } else if (mod_order == 6) { + QAM_amp128 = simde_mm_set1_epi16(QAM64_n1); // 4/sqrt{42} + QAM_amp128b = simde_mm_set1_epi16(QAM64_n2); // 2/sqrt{42} + QAM_amp128c = simde_mm_setzero_si128(); + } else if (mod_order == 8) { + QAM_amp128 = simde_mm_set1_epi16(QAM256_n1); + QAM_amp128b = simde_mm_set1_epi16(QAM256_n2); + QAM_amp128c = simde_mm_set1_epi16(QAM256_n3); + } + ch_mag128_0 = (simde__m128i *)ch_mag[0]; + ch_mag128b_0 = (simde__m128i *)ch_magb[0]; + ch_mag128c_0 = (simde__m128i *)ch_magc[0]; + ch_mag128_1 = (simde__m128i *)ch_mag[1]; + ch_mag128b_1 = (simde__m128i *)ch_magb[1]; + ch_mag128c_1 = (simde__m128i *)ch_magc[1]; + } + + for (int rb = 0; rb < 3 * nb_rb_0; rb++) { + // Magnitude computation + if (mod_order > 2) { + uint64_t sum_det = 0; + for (int k = 0; k < 4; k++) { + sum_det += (((uint32_t *)&determ_fin_128[0])[k]); + } + // Add bias to reduce rounding error + sum_det = (sum_det + 2) >> 2; + + int b = log2_approx(sum_det) - 8; + if (b > 0) { + mmtmpD2 = simde_mm_srai_epi32(determ_fin_128[0], b); + } else { + mmtmpD2 = simde_mm_slli_epi32(determ_fin_128[0], -b); + } + mmtmpD3 = simde_mm_unpacklo_epi32(mmtmpD2, mmtmpD2); + mmtmpD2 = simde_mm_unpackhi_epi32(mmtmpD2, mmtmpD2); + mmtmpD2 = simde_mm_packs_epi32(mmtmpD3, mmtmpD2); + + // Layer 0 + ch_mag128_0[0] = mmtmpD2; + ch_mag128b_0[0] = mmtmpD2; + ch_mag128c_0[0] = mmtmpD2; + ch_mag128_0[0] = simde_mm_mulhi_epi16(ch_mag128_0[0], QAM_amp128); + ch_mag128_0[0] = simde_mm_slli_epi16(ch_mag128_0[0], 1); + ch_mag128b_0[0] = simde_mm_mulhi_epi16(ch_mag128b_0[0], QAM_amp128b); + ch_mag128b_0[0] = simde_mm_slli_epi16(ch_mag128b_0[0], 1); + ch_mag128c_0[0] = simde_mm_mulhi_epi16(ch_mag128c_0[0], QAM_amp128c); + ch_mag128c_0[0] = simde_mm_slli_epi16(ch_mag128c_0[0], 1); + + // Layer 1 + ch_mag128_1[0] = mmtmpD2; + ch_mag128b_1[0] = mmtmpD2; + ch_mag128c_1[0] = mmtmpD2; + ch_mag128_1[0] = simde_mm_mulhi_epi16(ch_mag128_1[0], QAM_amp128); + ch_mag128_1[0] = simde_mm_slli_epi16(ch_mag128_1[0], 1); + ch_mag128b_1[0] = simde_mm_mulhi_epi16(ch_mag128b_1[0], QAM_amp128b); + ch_mag128b_1[0] = simde_mm_slli_epi16(ch_mag128b_1[0], 1); + ch_mag128c_1[0] = simde_mm_mulhi_epi16(ch_mag128c_1[0], QAM_amp128c); + ch_mag128c_1[0] = simde_mm_slli_epi16(ch_mag128c_1[0], 1); + } + + // multiply by channel Inv + // rxdataF_zf128_0 = rxdataF_comp128_0*d - b*rxdataF_comp128_1 + // rxdataF_zf128_1 = rxdataF_comp128_1*a - c*rxdataF_comp128_0 + // printf("layer_1 \n"); + mmtmpD0 = nr_comp_muli_sum(rxdataF_comp128_0[0], after_mf_d_128[0], rxdataF_comp128_1[0], after_mf_b_128[0], determ_fin_128[0]); + + // printf("layer_2 \n"); + mmtmpD1 = nr_comp_muli_sum(rxdataF_comp128_1[0], after_mf_a_128[0], rxdataF_comp128_0[0], after_mf_c_128[0], determ_fin_128[0]); + + rxdataF_comp128_0[0] = mmtmpD0; + rxdataF_comp128_1[0] = mmtmpD1; + +#ifdef DEBUG_DLSCH_DEMOD + printf("\n Rx signal after ZF l%d rb%d\n", symbol, rb); + print_shorts(" Rx layer 1:", (int16_t *)&rxdataF_comp128_0[0]); + print_shorts(" Rx layer 2:", (int16_t *)&rxdataF_comp128_1[0]); +#endif + determ_fin_128 += 1; + ch_mag128_0 += 1; + ch_mag128_1 += 1; + ch_mag128b_0 += 1; + ch_mag128b_1 += 1; + ch_mag128c_0 += 1; + ch_mag128c_1 += 1; + rxdataF_comp128_0 += 1; + rxdataF_comp128_1 += 1; + after_mf_a_128 += 1; + after_mf_b_128 += 1; + after_mf_c_128 += 1; + after_mf_d_128 += 1; + } + return (0); +} diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 2e8f56a8c5..7d7f9eb307 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -570,6 +570,9 @@ static int nr_ue_pdsch_procedures(PHY_VARS_NR_UE *ue, allocCast3D(dl_ch_magb, c16_t, toFree4, NR_SYMBOLS_PER_SLOT, dlsch->cw_info.Nl, rx_size_symbol, false); fourDimArray_t *toFree5 = NULL; allocCast3D(dl_ch_magr, c16_t, toFree5, NR_SYMBOLS_PER_SLOT, dlsch->cw_info.Nl, rx_size_symbol, false); + fourDimArray_t *toFreeRho = NULL; + allocCast3D(rho_dl, c16_t, toFreeRho, NR_SYMBOLS_PER_SLOT, dlsch->cw_info.Nl * dlsch->cw_info.Nl, rx_size_symbol, false); + for (int m = dlschCfg->start_symbol; m < (dlschCfg->number_symbols + dlschCfg->start_symbol); m++) { bool first_symbol_flag = false; if (m == first_symbol_with_data) @@ -601,7 +604,8 @@ static int nr_ue_pdsch_procedures(PHY_VARS_NR_UE *ue, ptrs_phase_per_slot, ptrs_re_per_slot, nvar, - &scope_req) + &scope_req, + rho_dl) < 0) { if (scope_req.copy_chanest_to_scope) { UEunlockScopeData(ue, pdschChanEstimates); @@ -624,6 +628,7 @@ static int nr_ue_pdsch_procedures(PHY_VARS_NR_UE *ue, free(toFree3); free(toFree4); free(toFree5); + free(toFreeRho); return 0; }