mirror of https://github.com/pvnis/srsRAN_4G.git
New optimization on the PHY for both UE and eNodeB (#251)
* New parallel Turbodecoder implementation in SSE/AVX 16-bit and 8-bit * Optimised UL Interleaver * Include TB CRC calculation in FEC encoder * New threading prioritiesmaster
parent
695990f297
commit
bc9d342959
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsLTE library.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSLTE_TURBODECODER_IMPL_H
|
||||||
|
#define SRSLTE_TURBODECODER_IMPL_H
|
||||||
|
|
||||||
|
#include "srslte/config.h"
|
||||||
|
|
||||||
|
/* Interface for internal decoder implementation */
|
||||||
|
typedef enum SRSLTE_API {
|
||||||
|
SRSLTE_TDEC_AUTO = 0,
|
||||||
|
SRSLTE_TDEC_GENERIC,
|
||||||
|
SRSLTE_TDEC_SSE,
|
||||||
|
SRSLTE_TDEC_SSE_WINDOW,
|
||||||
|
SRSLTE_TDEC_AVX_WINDOW,
|
||||||
|
SRSLTE_TDEC_SSE8_WINDOW,
|
||||||
|
SRSLTE_TDEC_AVX8_WINDOW,
|
||||||
|
SRSLTE_TDEC_NOF_IMP
|
||||||
|
} srslte_tdec_impl_type_t;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LLR_IS_8BIT
|
||||||
|
#define llr_t int8_t
|
||||||
|
#define type_name srslte_tdec_8bit_impl_t
|
||||||
|
#else
|
||||||
|
#ifdef LLR_IS_16BIT
|
||||||
|
#define llr_t int16_t
|
||||||
|
#define type_name srslte_tdec_16bit_impl_t
|
||||||
|
#else
|
||||||
|
#error "Unsupported LLR mode"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct SRSLTE_API {
|
||||||
|
int (*tdec_init)(void **h, uint32_t max_long_cb);
|
||||||
|
void (*tdec_free)(void *h);
|
||||||
|
void (*tdec_dec)(void *h, llr_t * input, llr_t *app, llr_t * parity, llr_t *output, uint32_t long_cb);
|
||||||
|
void (*tdec_extract_input)(llr_t *input, llr_t *syst, llr_t *parity0, llr_t *parity1, llr_t *app2, uint32_t long_cb);
|
||||||
|
void (*tdec_decision_byte)(llr_t *app1, uint8_t *output, uint32_t long_cb);
|
||||||
|
} type_name;
|
||||||
|
|
||||||
|
#undef llr_t
|
||||||
|
#undef type_name
|
@ -0,0 +1,158 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsLTE library.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srslte/config.h"
|
||||||
|
|
||||||
|
#define MAKE_CALL(a) CONCAT2(a,type_name)
|
||||||
|
#define MAKE_VEC(a) CONCAT2(a,vec_suffix)
|
||||||
|
#define PRINT CONCAT2(srslte_vec_fprint,print_suffix)
|
||||||
|
|
||||||
|
#ifdef LLR_IS_8BIT
|
||||||
|
#define llr_t int8_t
|
||||||
|
#define type_name _8bit
|
||||||
|
#define vec_suffix _bbb
|
||||||
|
#define print_suffix _bs
|
||||||
|
#define decptr h->dec8[h->current_dec]
|
||||||
|
#define dechdlr h->dec8_hdlr[h->current_dec]
|
||||||
|
#define input_is_interleaved 1
|
||||||
|
#else
|
||||||
|
#ifdef LLR_IS_16BIT
|
||||||
|
#define llr_t int16_t
|
||||||
|
#define vec_suffix _sss
|
||||||
|
#define print_suffix _s
|
||||||
|
#define decptr h->dec16[h->current_dec]
|
||||||
|
#define dechdlr h->dec16_hdlr[h->current_dec]
|
||||||
|
#define input_is_interleaved (h->current_dec > 0)
|
||||||
|
#define type_name _16bit
|
||||||
|
#else
|
||||||
|
#warning "Unsupported LLR mode"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define debug_enabled_iter 0
|
||||||
|
#define debug_len 20
|
||||||
|
|
||||||
|
#define debug_vec(a) if (debug_enabled_iter) {printf("%s it=%d: ", STRING(a), n_iter);PRINT(stdout, a, debug_len);}
|
||||||
|
|
||||||
|
|
||||||
|
static void MAKE_CALL(extract_input_tail_sb)(llr_t *input, llr_t *syst, llr_t *app2, llr_t *parity0, llr_t *parity1, uint32_t long_cb)
|
||||||
|
{
|
||||||
|
for (int i = long_cb; i < long_cb + 3; i++) {
|
||||||
|
syst[i] = input[3*(long_cb+32) + 2*(i - long_cb)];
|
||||||
|
parity0[i] = input[3*(long_cb+32)+ 2*(i - long_cb) + 1];
|
||||||
|
|
||||||
|
app2[i] = input[3*(long_cb+32) + 6 + 2*(i - long_cb)];
|
||||||
|
parity1[i] = input[3*(long_cb+32) + 6 + 2*(i - long_cb) + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Runs 1 turbo decoder iteration */
|
||||||
|
void MAKE_CALL(run_tdec_iteration)(srslte_tdec_t * h, llr_t * input)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (h->current_cbidx >= 0) {
|
||||||
|
uint16_t *inter = h->interleaver[h->current_inter_idx][h->current_cbidx].forward;
|
||||||
|
uint16_t *deinter = h->interleaver[h->current_inter_idx][h->current_cbidx].reverse;
|
||||||
|
llr_t *syst = (llr_t*) h->syst0;
|
||||||
|
llr_t *parity0 = (llr_t*) h->parity0;
|
||||||
|
llr_t *parity1 = (llr_t*) h->parity1;
|
||||||
|
|
||||||
|
llr_t *app1 = (llr_t*) h->app1;
|
||||||
|
llr_t *app2 = (llr_t*) h->app2;
|
||||||
|
llr_t *ext1 = (llr_t*) h->ext1;
|
||||||
|
llr_t *ext2 = (llr_t*) h->ext2;
|
||||||
|
|
||||||
|
uint32_t long_cb = h->current_long_cb;
|
||||||
|
uint32_t n_iter = h->n_iter;
|
||||||
|
|
||||||
|
if (SRSLTE_TDEC_EXPECT_INPUT_SB && !h->force_not_sb && input_is_interleaved) {
|
||||||
|
syst = input;
|
||||||
|
// align to 32 bytes (warning: must be same alignment as in rm_turbo.c)
|
||||||
|
parity0 = &input[long_cb+32];
|
||||||
|
parity1 = &input[2*(long_cb+32)];
|
||||||
|
if (n_iter == 0) {
|
||||||
|
MAKE_CALL(extract_input_tail_sb)(input, syst, app2, parity0, parity1, long_cb);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (n_iter == 0) {
|
||||||
|
decptr->tdec_extract_input(input, syst, app2, parity0, parity1, long_cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((n_iter%2) == 0) {
|
||||||
|
|
||||||
|
// Add apriori information to decoder 1
|
||||||
|
if (n_iter) {
|
||||||
|
MAKE_VEC(srslte_vec_sub)(app1, ext1, app1, long_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run MAP DEC #1
|
||||||
|
decptr->tdec_dec(dechdlr, syst, n_iter ? app1 : NULL, parity0, ext1, long_cb);
|
||||||
|
|
||||||
|
}
|
||||||
|
// Interleave extrinsic output of DEC1 to form apriori info for decoder 2
|
||||||
|
if (n_iter%2) {
|
||||||
|
// Convert aposteriori information into extrinsic information
|
||||||
|
if (n_iter > 1) {
|
||||||
|
MAKE_VEC(srslte_vec_sub)(ext1, app1, ext1, long_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
MAKE_VEC(srslte_vec_lut)(ext1, deinter, app2, long_cb);
|
||||||
|
|
||||||
|
// Run MAP DEC #2. 2nd decoder uses apriori information as systematic bits
|
||||||
|
decptr->tdec_dec(dechdlr, app2, NULL, parity1, ext2, long_cb);
|
||||||
|
|
||||||
|
// Deinterleaved extrinsic bits become apriori info for decoder 1
|
||||||
|
MAKE_VEC(srslte_vec_lut)(ext2, inter, app1, long_cb);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (h->n_iter == 0) {
|
||||||
|
debug_vec(syst);
|
||||||
|
debug_vec(parity0);
|
||||||
|
debug_vec(parity1);
|
||||||
|
}
|
||||||
|
debug_vec(ext1);
|
||||||
|
debug_vec(ext2);
|
||||||
|
debug_vec(app1);
|
||||||
|
debug_vec(app2);
|
||||||
|
|
||||||
|
h->n_iter++;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Error CB index not set (call srslte_tdec_new_cb() first\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef debug_enabled
|
||||||
|
#undef debug_len
|
||||||
|
#undef debug_vec
|
||||||
|
#undef llr_t
|
||||||
|
#undef vec_suffix
|
||||||
|
#undef print_suffix
|
||||||
|
#undef decptr
|
||||||
|
#undef dechdlr
|
||||||
|
#undef type_name
|
||||||
|
#undef input_is_interleaved
|
@ -1,122 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2015 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the srsLTE library.
|
|
||||||
*
|
|
||||||
* srsLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**********************************************************************************************
|
|
||||||
* File: turbodecoder.h
|
|
||||||
*
|
|
||||||
* Description: Turbo Decoder.
|
|
||||||
* Parallel Concatenated Convolutional Code (PCCC) with two 8-state constituent
|
|
||||||
* encoders and one turbo code internal interleaver. The coding rate of turbo
|
|
||||||
* encoder is 1/3.
|
|
||||||
* MAP_GEN is the MAX-LOG-MAP generic implementation of the decoder.
|
|
||||||
*
|
|
||||||
* Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.1.3.2
|
|
||||||
*********************************************************************************************/
|
|
||||||
|
|
||||||
#ifndef SRSLTE_TURBODECODER_SIMD_H
|
|
||||||
#define SRSLTE_TURBODECODER_SIMD_H
|
|
||||||
|
|
||||||
#include "srslte/config.h"
|
|
||||||
#include "srslte/phy/fec/tc_interl.h"
|
|
||||||
#include "srslte/phy/fec/cbsegm.h"
|
|
||||||
|
|
||||||
// Define maximum number of CB decoded in parallel (2 for AVX2)
|
|
||||||
#define SRSLTE_TDEC_MAX_NPAR 2
|
|
||||||
|
|
||||||
#define SRSLTE_TCOD_RATE 3
|
|
||||||
#define SRSLTE_TCOD_TOTALTAIL 12
|
|
||||||
|
|
||||||
#define SRSLTE_TCOD_MAX_LEN_CB 6144
|
|
||||||
#define SRSLTE_TCOD_MAX_LEN_CODED (SRSLTE_TCOD_RATE*SRSLTE_TCOD_MAX_LEN_CB+SRSLTE_TCOD_TOTALTAIL)
|
|
||||||
|
|
||||||
typedef struct SRSLTE_API {
|
|
||||||
uint32_t max_long_cb;
|
|
||||||
uint32_t max_par_cb;
|
|
||||||
int16_t *alpha;
|
|
||||||
int16_t *branch;
|
|
||||||
} map_gen_t;
|
|
||||||
|
|
||||||
typedef struct SRSLTE_API {
|
|
||||||
uint32_t max_long_cb;
|
|
||||||
uint32_t max_par_cb;
|
|
||||||
|
|
||||||
map_gen_t dec;
|
|
||||||
|
|
||||||
int16_t *app1[SRSLTE_TDEC_MAX_NPAR];
|
|
||||||
int16_t *app2[SRSLTE_TDEC_MAX_NPAR];
|
|
||||||
int16_t *ext1[SRSLTE_TDEC_MAX_NPAR];
|
|
||||||
int16_t *ext2[SRSLTE_TDEC_MAX_NPAR];
|
|
||||||
int16_t *syst[SRSLTE_TDEC_MAX_NPAR];
|
|
||||||
int16_t *parity0[SRSLTE_TDEC_MAX_NPAR];
|
|
||||||
int16_t *parity1[SRSLTE_TDEC_MAX_NPAR];
|
|
||||||
|
|
||||||
int cb_mask;
|
|
||||||
int current_cbidx;
|
|
||||||
srslte_tc_interl_t interleaver[SRSLTE_NOF_TC_CB_SIZES];
|
|
||||||
int n_iter[SRSLTE_TDEC_MAX_NPAR];
|
|
||||||
} srslte_tdec_simd_t;
|
|
||||||
|
|
||||||
SRSLTE_API int srslte_tdec_simd_init(srslte_tdec_simd_t * h,
|
|
||||||
uint32_t max_par_cb,
|
|
||||||
uint32_t max_long_cb);
|
|
||||||
|
|
||||||
SRSLTE_API void srslte_tdec_simd_free(srslte_tdec_simd_t * h);
|
|
||||||
|
|
||||||
SRSLTE_API int srslte_tdec_simd_reset(srslte_tdec_simd_t * h,
|
|
||||||
uint32_t long_cb);
|
|
||||||
|
|
||||||
SRSLTE_API
|
|
||||||
|
|
||||||
SRSLTE_API int srslte_tdec_simd_get_nof_iterations_cb(srslte_tdec_simd_t * h,
|
|
||||||
uint32_t cb_idx);
|
|
||||||
|
|
||||||
SRSLTE_API int srslte_tdec_simd_reset_cb(srslte_tdec_simd_t * h,
|
|
||||||
uint32_t cb_idx);
|
|
||||||
|
|
||||||
SRSLTE_API void srslte_tdec_simd_iteration(srslte_tdec_simd_t * h,
|
|
||||||
int16_t * input[SRSLTE_TDEC_MAX_NPAR],
|
|
||||||
uint32_t long_cb);
|
|
||||||
|
|
||||||
SRSLTE_API void srslte_tdec_simd_decision(srslte_tdec_simd_t * h,
|
|
||||||
uint8_t *output[SRSLTE_TDEC_MAX_NPAR],
|
|
||||||
uint32_t long_cb);
|
|
||||||
|
|
||||||
SRSLTE_API void srslte_tdec_simd_decision_byte(srslte_tdec_simd_t * h,
|
|
||||||
uint8_t *output[SRSLTE_TDEC_MAX_NPAR],
|
|
||||||
uint32_t long_cb);
|
|
||||||
|
|
||||||
SRSLTE_API void srslte_tdec_simd_decision_byte_cb(srslte_tdec_simd_t * h,
|
|
||||||
uint8_t *output,
|
|
||||||
uint32_t cbidx,
|
|
||||||
uint32_t long_cb);
|
|
||||||
|
|
||||||
SRSLTE_API int srslte_tdec_simd_run_all(srslte_tdec_simd_t * h,
|
|
||||||
int16_t * input[SRSLTE_TDEC_MAX_NPAR],
|
|
||||||
uint8_t *output[SRSLTE_TDEC_MAX_NPAR],
|
|
||||||
uint32_t nof_iterations,
|
|
||||||
uint32_t long_cb);
|
|
||||||
|
|
||||||
#endif // SRSLTE_TURBODECODER_SIMD_H
|
|
@ -1,119 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2015 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the srsLTE library.
|
|
||||||
*
|
|
||||||
* srsLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**********************************************************************************************
|
|
||||||
* File: turbodecoder.h
|
|
||||||
*
|
|
||||||
* Description: Turbo Decoder.
|
|
||||||
* Parallel Concatenated Convolutional Code (PCCC) with two 8-state constituent
|
|
||||||
* encoders and one turbo code internal interleaver. The coding rate of turbo
|
|
||||||
* encoder is 1/3.
|
|
||||||
* MAP_GEN is the MAX-LOG-MAP generic implementation of the decoder.
|
|
||||||
*
|
|
||||||
* Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.1.3.2
|
|
||||||
*********************************************************************************************/
|
|
||||||
|
|
||||||
#ifndef SRSLTE_TURBODECODER_SIMD_INTER_H
|
|
||||||
#define SRSLTE_TURBODECODER_SIMD_INTER_H
|
|
||||||
|
|
||||||
|
|
||||||
/** This is an simd inter-frame parallel turbo decoder. Parallizes 8 code-blocks using SSE
|
|
||||||
* This implementation is currently not functional and not used by the rest of the code
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "srslte/config.h"
|
|
||||||
#include "srslte/phy/fec/tc_interl.h"
|
|
||||||
#include "srslte/phy/fec/cbsegm.h"
|
|
||||||
|
|
||||||
#if LV_HAVE_AVX2
|
|
||||||
#define SRSLTE_TDEC_MAX_NPAR 16
|
|
||||||
#else
|
|
||||||
#define SRSLTE_TDEC_MAX_NPAR 8
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct SRSLTE_API {
|
|
||||||
int max_long_cb;
|
|
||||||
|
|
||||||
int16_t *syst0;
|
|
||||||
int16_t *parity0;
|
|
||||||
int16_t *syst1;
|
|
||||||
int16_t *parity1;
|
|
||||||
int16_t *llr1;
|
|
||||||
int16_t *llr2;
|
|
||||||
int16_t *w;
|
|
||||||
int16_t *alpha;
|
|
||||||
|
|
||||||
uint32_t max_par_cb;
|
|
||||||
int current_cbidx;
|
|
||||||
uint32_t current_long_cb;
|
|
||||||
srslte_tc_interl_t interleaver[SRSLTE_NOF_TC_CB_SIZES];
|
|
||||||
int n_iter[SRSLTE_TDEC_MAX_NPAR];
|
|
||||||
} srslte_tdec_simd_inter_t;
|
|
||||||
|
|
||||||
SRSLTE_API int srslte_tdec_simd_inter_init(srslte_tdec_simd_inter_t * h,
|
|
||||||
uint32_t max_par_cb,
|
|
||||||
uint32_t max_long_cb);
|
|
||||||
|
|
||||||
SRSLTE_API void srslte_tdec_simd_inter_free(srslte_tdec_simd_inter_t * h);
|
|
||||||
|
|
||||||
SRSLTE_API int srslte_tdec_simd_inter_reset(srslte_tdec_simd_inter_t * h,
|
|
||||||
uint32_t long_cb);
|
|
||||||
|
|
||||||
SRSLTE_API int srslte_tdec_simd_inter_get_nof_iterations_cb(srslte_tdec_simd_inter_t * h,
|
|
||||||
uint32_t cb_idx);
|
|
||||||
|
|
||||||
SRSLTE_API int srslte_tdec_simd_inter_reset_cb(srslte_tdec_simd_inter_t * h,
|
|
||||||
uint32_t cb_idx);
|
|
||||||
|
|
||||||
SRSLTE_API void srslte_tdec_simd_inter_iteration(srslte_tdec_simd_inter_t * h,
|
|
||||||
int16_t * input[SRSLTE_TDEC_MAX_NPAR],
|
|
||||||
uint32_t nof_cb,
|
|
||||||
uint32_t long_cb);
|
|
||||||
|
|
||||||
SRSLTE_API void srslte_tdec_simd_inter_decision(srslte_tdec_simd_inter_t * h,
|
|
||||||
uint8_t *output[SRSLTE_TDEC_MAX_NPAR],
|
|
||||||
uint32_t nof_cb,
|
|
||||||
uint32_t long_cb);
|
|
||||||
|
|
||||||
SRSLTE_API void srslte_tdec_simd_inter_decision_byte(srslte_tdec_simd_inter_t * h,
|
|
||||||
uint8_t *output[SRSLTE_TDEC_MAX_NPAR],
|
|
||||||
uint32_t nof_cb,
|
|
||||||
uint32_t long_cb);
|
|
||||||
|
|
||||||
SRSLTE_API void srslte_tdec_simd_inter_decision_byte_cb(srslte_tdec_simd_inter_t * h,
|
|
||||||
uint8_t *output,
|
|
||||||
uint32_t cbidx,
|
|
||||||
uint32_t long_cb);
|
|
||||||
|
|
||||||
SRSLTE_API int srslte_tdec_simd_inter_run_all(srslte_tdec_simd_inter_t * h,
|
|
||||||
int16_t *input[SRSLTE_TDEC_MAX_NPAR],
|
|
||||||
uint8_t *output[SRSLTE_TDEC_MAX_NPAR],
|
|
||||||
uint32_t nof_iterations,
|
|
||||||
uint32_t nof_cb,
|
|
||||||
uint32_t long_cb);
|
|
||||||
|
|
||||||
#endif // SRSLTE_TURBODECODER_SIMD_INTER_H
|
|
@ -0,0 +1,750 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsLTE library.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srslte/config.h"
|
||||||
|
|
||||||
|
#define MAKE_FUNC(a) CONCAT2(CONCAT2(tdec_win,WINIMP),CONCAT2(_,a))
|
||||||
|
#define MAKE_TYPE CONCAT2(CONCAT2(tdec_win_,WINIMP),_t)
|
||||||
|
|
||||||
|
#ifdef WINIMP_IS_SSE16
|
||||||
|
|
||||||
|
#ifndef LV_HAVE_SSE
|
||||||
|
#error "Selected SSE window decoder but instruction set not supported"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <nmmintrin.h>
|
||||||
|
|
||||||
|
#define WINIMP sse16
|
||||||
|
#define nof_blocks 8
|
||||||
|
|
||||||
|
#define llr_t int16_t
|
||||||
|
|
||||||
|
#define simd_type_t __m128i
|
||||||
|
#define simd_load _mm_load_si128
|
||||||
|
#define simd_store _mm_store_si128
|
||||||
|
#define simd_add _mm_adds_epi16
|
||||||
|
#define simd_sub _mm_subs_epi16
|
||||||
|
#define simd_max _mm_max_epi16
|
||||||
|
#define simd_set1 _mm_set1_epi16
|
||||||
|
#define simd_insert _mm_insert_epi16
|
||||||
|
#define simd_shuffle _mm_shuffle_epi8
|
||||||
|
#define move_right _mm_set_epi8(15,14,15,14,13,12,11,10,9,8,7,6,5,4,3,2)
|
||||||
|
#define move_left _mm_set_epi8(13,12,11,10,9,8,7,6,5,4,3,2,1,0,1,0)
|
||||||
|
#define simd_rb_shift _mm_srai_epi16
|
||||||
|
|
||||||
|
#define normalize_period 2
|
||||||
|
#define win_overlap_len 40
|
||||||
|
|
||||||
|
#define divide_output 1
|
||||||
|
|
||||||
|
#define INF 10000
|
||||||
|
|
||||||
|
#else
|
||||||
|
#ifdef WINIMP_IS_AVX16
|
||||||
|
|
||||||
|
#ifndef LV_HAVE_AVX
|
||||||
|
#error "Selected AVX window decoder but instruction set not supported"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <immintrin.h>
|
||||||
|
|
||||||
|
#define WINIMP avx16
|
||||||
|
#define nof_blocks 16
|
||||||
|
|
||||||
|
#define llr_t int16_t
|
||||||
|
|
||||||
|
#define simd_type_t __m256i
|
||||||
|
#define simd_load _mm256_load_si256
|
||||||
|
#define simd_store _mm256_store_si256
|
||||||
|
#define simd_add _mm256_adds_epi16
|
||||||
|
#define simd_sub _mm256_subs_epi16
|
||||||
|
#define simd_max _mm256_max_epi16
|
||||||
|
#define simd_set1 _mm256_set1_epi16
|
||||||
|
#define simd_insert _mm256_insert_epi16
|
||||||
|
#define simd_shuffle _mm256_shuffle_epi8
|
||||||
|
#define move_right _mm256_set_epi8(31,30,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2)
|
||||||
|
#define move_left _mm256_set_epi8(29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,1,0)
|
||||||
|
|
||||||
|
#define normalize_period 2
|
||||||
|
#define win_overlap_len 40
|
||||||
|
|
||||||
|
#define INF 10000
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifdef WINIMP_IS_SSE8
|
||||||
|
|
||||||
|
#ifndef LV_HAVE_SSE
|
||||||
|
#error "Selected SSE window decoder but instruction set not supported"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <nmmintrin.h>
|
||||||
|
|
||||||
|
#define WINIMP sse8
|
||||||
|
#define nof_blocks 16
|
||||||
|
|
||||||
|
#define llr_t int8_t
|
||||||
|
|
||||||
|
#define simd_type_t __m128i
|
||||||
|
#define simd_load _mm_load_si128
|
||||||
|
#define simd_store _mm_store_si128
|
||||||
|
#define simd_add _mm_adds_epi8
|
||||||
|
#define simd_sub _mm_subs_epi8
|
||||||
|
#define simd_max _mm_max_epi8
|
||||||
|
#define simd_set1 _mm_set1_epi8
|
||||||
|
#define simd_insert _mm_insert_epi8
|
||||||
|
#define simd_shuffle _mm_shuffle_epi8
|
||||||
|
#define move_right _mm_set_epi8(15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1)
|
||||||
|
#define move_left _mm_set_epi8(14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0)
|
||||||
|
#define simd_rb_shift simd_rb_shift_128
|
||||||
|
|
||||||
|
#define normalize_max
|
||||||
|
#define normalize_period 1
|
||||||
|
#define win_overlap_len 40
|
||||||
|
#define use_saturated_add
|
||||||
|
#define divide_output 1
|
||||||
|
|
||||||
|
#define INF 0
|
||||||
|
|
||||||
|
inline static simd_type_t simd_rb_shift_128(simd_type_t v, const int l) {
|
||||||
|
__m128i low = _mm_srai_epi16(_mm_slli_epi16(v,8), l+8);
|
||||||
|
__m128i hi = _mm_srai_epi16(v,l);
|
||||||
|
return _mm_blendv_epi8(hi, low, _mm_set1_epi32(0x00FF00FF));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifdef WINIMP_IS_AVX8
|
||||||
|
|
||||||
|
#ifndef LV_HAVE_AVX
|
||||||
|
#error "Selected AVX window decoder but instruction set not supported"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <immintrin.h>
|
||||||
|
|
||||||
|
#define WINIMP avx8
|
||||||
|
#define nof_blocks 32
|
||||||
|
|
||||||
|
#define llr_t int8_t
|
||||||
|
|
||||||
|
#define simd_type_t __m256i
|
||||||
|
#define simd_load _mm256_load_si256
|
||||||
|
#define simd_store _mm256_store_si256
|
||||||
|
#define simd_add _mm256_adds_epi8
|
||||||
|
#define simd_sub _mm256_subs_epi8
|
||||||
|
#define simd_max _mm256_max_epi8
|
||||||
|
#define simd_set1 _mm256_set1_epi8
|
||||||
|
#define simd_insert _mm256_insert_epi8
|
||||||
|
#define simd_shuffle _mm256_shuffle_epi8
|
||||||
|
#define move_right _mm256_set_epi8(31,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1)
|
||||||
|
#define move_left _mm256_set_epi8(30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0)
|
||||||
|
#define simd_rb_shift simd_rb_shift_256
|
||||||
|
|
||||||
|
#define INF 0
|
||||||
|
|
||||||
|
#define normalize_max
|
||||||
|
#define normalize_period 1
|
||||||
|
#define win_overlap_len 40
|
||||||
|
#define use_saturated_add
|
||||||
|
#define divide_output 1
|
||||||
|
|
||||||
|
inline static simd_type_t simd_rb_shift_256(simd_type_t v, const int l) {
|
||||||
|
__m256i low = _mm256_srai_epi16(_mm256_slli_epi16(v,8), l+8);
|
||||||
|
__m256i hi = _mm256_srai_epi16(v,l);
|
||||||
|
return _mm256_blendv_epi8(hi, low, _mm256_set1_epi32(0x00FF00FF));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Unknown WINIMP value"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct SRSLTE_API {
|
||||||
|
uint32_t max_long_cb;
|
||||||
|
llr_t *beta;
|
||||||
|
} MAKE_TYPE;
|
||||||
|
|
||||||
|
|
||||||
|
#define long_sb (long_cb/nof_blocks)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define debug_enabled_win 0
|
||||||
|
|
||||||
|
#if debug_enabled_win
|
||||||
|
#define debug_state(d) printf("k=%5d, in=%5d, pa=%3d, out=%5d, alpha=[", d*long_sb+k+1, MAKE_FUNC(get_simd)(x,d), MAKE_FUNC(get_simd)(y,d), MAKE_FUNC(get_simd)(out,d)); \
|
||||||
|
for (int j=0;j<8;j++) printf("%5d, ", MAKE_FUNC(get_simd)(old[j],d)); \
|
||||||
|
printf("], beta=["); \
|
||||||
|
for (int j=0;j<8;j++) printf("%5d, ", MAKE_FUNC(get_simd)(beta_save[j], d));printf("\n");
|
||||||
|
|
||||||
|
#define debug_state_pre(d) printf("pre-window k=%5d, in=%5d, pa=%3d, alpha=[", (d+1)*long_sb-loop_len+k+1, MAKE_FUNC(get_simd)(x,d), MAKE_FUNC(get_simd)(y,d)); \
|
||||||
|
for (int j=0;j<8;j++) printf("%5d, ", MAKE_FUNC(get_simd)(old[j],d)); \
|
||||||
|
printf("]\n");
|
||||||
|
|
||||||
|
#define debug_state_beta(d) printf("k=%5d, in=%5d, pa=%3d, beta=[", d*long_sb+k, MAKE_FUNC(get_simd)(x,d), MAKE_FUNC(get_simd)(y,d)); \
|
||||||
|
for (int j=0;j<8;j++) printf("%5d, ", MAKE_FUNC(get_simd)(old[j],d));\
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
static llr_t MAKE_FUNC(get_simd)(simd_type_t x, uint32_t pos) {
|
||||||
|
llr_t *s = (llr_t*) &x;
|
||||||
|
return s[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define debug_state(a)
|
||||||
|
#define debug_state_pre(a)
|
||||||
|
#define debug_state_beta(a)
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
static void MAKE_FUNC(print_simd)(simd_type_t x) {
|
||||||
|
llr_t *s = (llr_t*) &x;
|
||||||
|
printf("[");
|
||||||
|
for (int i=0;i<nof_blocks;i++) {
|
||||||
|
printf("%4d, ", s[i]);
|
||||||
|
}
|
||||||
|
printf("]\n");
|
||||||
|
}*/
|
||||||
|
|
||||||
|
inline static llr_t MAKE_FUNC(sadd)(llr_t x, llr_t y) {
|
||||||
|
#ifndef use_saturated_add
|
||||||
|
return x+y;
|
||||||
|
#else
|
||||||
|
int16_t z = (int16_t) x+y;
|
||||||
|
return z>127?127:(int8_t) z;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void MAKE_FUNC(normalize)(uint32_t k, simd_type_t old[8]) {
|
||||||
|
if ((k % normalize_period) == 0 && k != 0) {
|
||||||
|
#ifdef normalize_max
|
||||||
|
simd_type_t m = simd_max(old[0],old[1]);
|
||||||
|
for (int i=2;i<8;i++) {
|
||||||
|
m = simd_max(m,old[i]);
|
||||||
|
}
|
||||||
|
for (int i=0;i<8;i++) {
|
||||||
|
old[i] = simd_sub(old[i], m);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for (int i = 1; i < 8; i++) {
|
||||||
|
old[i] = simd_sub(old[i], old[0]);
|
||||||
|
}
|
||||||
|
old[0] = simd_set1(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MAKE_FUNC(beta_trellis)(llr_t *input, llr_t *parity, uint32_t long_cb, llr_t old[8])
|
||||||
|
{
|
||||||
|
llr_t m_b[8], new[8];
|
||||||
|
llr_t x, y, xy;
|
||||||
|
|
||||||
|
/* Calculate last state using Tail. No need to use SIMD here */
|
||||||
|
old[0] = 0;
|
||||||
|
for (int i = 1; i < 8; i++) {
|
||||||
|
old[i] = -INF;
|
||||||
|
}
|
||||||
|
for (int k=long_cb+2;k >= long_cb; k--) {
|
||||||
|
x = input[k];
|
||||||
|
y = parity[k];
|
||||||
|
|
||||||
|
xy = MAKE_FUNC(sadd)(x, y);
|
||||||
|
|
||||||
|
m_b[0] = MAKE_FUNC(sadd)(old[4],xy);
|
||||||
|
m_b[1] = old[4];
|
||||||
|
m_b[2] = MAKE_FUNC(sadd)(old[5], y);
|
||||||
|
m_b[3] = MAKE_FUNC(sadd)(old[5], x);
|
||||||
|
m_b[4] = MAKE_FUNC(sadd)(old[6], x);
|
||||||
|
m_b[5] = MAKE_FUNC(sadd)(old[6], y);
|
||||||
|
m_b[6] = old[7];
|
||||||
|
m_b[7] = MAKE_FUNC(sadd)(old[7], xy);
|
||||||
|
|
||||||
|
new[0] = old[0];
|
||||||
|
new[1] = MAKE_FUNC(sadd)(old[0], xy);
|
||||||
|
new[2] = MAKE_FUNC(sadd)(old[1], x);
|
||||||
|
new[3] = MAKE_FUNC(sadd)(old[1], y);
|
||||||
|
new[4] = MAKE_FUNC(sadd)(old[2], y);
|
||||||
|
new[5] = MAKE_FUNC(sadd)(old[2], x);
|
||||||
|
new[6] = MAKE_FUNC(sadd)(old[3], xy);
|
||||||
|
new[7] = old[3];
|
||||||
|
|
||||||
|
#if debug_enabled_win
|
||||||
|
printf("trellis: k=%d, in=%d, pa=%d, beta: ", k, x, y); for (int i=0;i<8;i++) {printf("%d,", old[i]);} printf("\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
if (m_b[i] > new[i])
|
||||||
|
new[i] = m_b[i];
|
||||||
|
old[i] = new[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Computes beta values */
|
||||||
|
static void MAKE_FUNC(beta)(MAKE_TYPE * s, llr_t *input, llr_t *app, llr_t *parity, uint32_t long_cb)
|
||||||
|
{
|
||||||
|
simd_type_t m_b[8], new[8], old[8];
|
||||||
|
simd_type_t x, y, xy, ap;
|
||||||
|
|
||||||
|
simd_type_t *inputPtr;
|
||||||
|
simd_type_t *appPtr;
|
||||||
|
simd_type_t *parityPtr;
|
||||||
|
simd_type_t *betaPtr = (simd_type_t*) s->beta;
|
||||||
|
|
||||||
|
uint32_t loop_len;
|
||||||
|
for (int j=0;j<2;j++) {
|
||||||
|
|
||||||
|
// First run L states to find initial state for all sub-blocks after first
|
||||||
|
if (j==0) {
|
||||||
|
loop_len = win_overlap_len;
|
||||||
|
} else {
|
||||||
|
loop_len = long_sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When passing through all window pick estimated initial states (known state for sb=0)
|
||||||
|
if (loop_len == long_sb) {
|
||||||
|
|
||||||
|
// shuffle across 128-bit boundary manually
|
||||||
|
#ifdef WINIMP_IS_AVX16
|
||||||
|
llr_t tmp[8];
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
tmp[i] = _mm256_extract_epi16(old[i], 8);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef WINIMP_IS_AVX8
|
||||||
|
llr_t tmp[8];
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
tmp[i] = _mm256_extract_epi8(old[i], 16);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
old[i] = simd_shuffle(old[i], move_right);
|
||||||
|
}
|
||||||
|
// last sub-block state is calculated from the trellis
|
||||||
|
llr_t trellis_old[8];
|
||||||
|
MAKE_FUNC(beta_trellis)(input, parity, long_cb, trellis_old);
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
old[i] = simd_insert(old[i], trellis_old[i], nof_blocks-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WINIMP_IS_AVX16
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
old[i] = _mm256_insert_epi16(old[i], tmp[i], 7);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef WINIMP_IS_AVX8
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
old[i] = _mm256_insert_epi8(old[i], tmp[i], 15);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inputPtr = (simd_type_t*) &input[long_cb-nof_blocks];
|
||||||
|
appPtr = (simd_type_t*) &app[long_cb-nof_blocks];
|
||||||
|
parityPtr = (simd_type_t*) &parity[long_cb-nof_blocks];
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
simd_store(&betaPtr[8*long_sb + i], old[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// when estimating states, just set all to unknown
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
old[i] = simd_set1(-INF);
|
||||||
|
}
|
||||||
|
inputPtr = (simd_type_t*) &input[nof_blocks*(loop_len-1)];
|
||||||
|
appPtr = (simd_type_t*) &app[nof_blocks*(loop_len-1)];
|
||||||
|
parityPtr = (simd_type_t*) &parity[nof_blocks*(loop_len-1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int k = loop_len - 1; k >= 0; k--) {
|
||||||
|
x = simd_load(inputPtr--);
|
||||||
|
y = simd_load(parityPtr--);
|
||||||
|
|
||||||
|
if (app) {
|
||||||
|
ap = simd_load(appPtr--);
|
||||||
|
x = simd_add(ap, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
xy = simd_add(x, y);
|
||||||
|
|
||||||
|
m_b[0] = simd_add(old[4], xy);
|
||||||
|
m_b[1] = old[4];
|
||||||
|
m_b[2] = simd_add(old[5], y);
|
||||||
|
m_b[3] = simd_add(old[5], x);
|
||||||
|
m_b[4] = simd_add(old[6], x);
|
||||||
|
m_b[5] = simd_add(old[6], y);
|
||||||
|
m_b[6] = old[7];
|
||||||
|
m_b[7] = simd_add(old[7], xy);
|
||||||
|
|
||||||
|
new[0] = old[0];
|
||||||
|
new[1] = simd_add(old[0], xy);
|
||||||
|
new[2] = simd_add(old[1], x);
|
||||||
|
new[3] = simd_add(old[1], y);
|
||||||
|
new[4] = simd_add(old[2], y);
|
||||||
|
new[5] = simd_add(old[2], x);
|
||||||
|
new[6] = simd_add(old[3], xy);
|
||||||
|
new[7] = old[3];
|
||||||
|
|
||||||
|
// Calculate maximum metric
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
old[i] = simd_max(m_b[i], new[i]);
|
||||||
|
}
|
||||||
|
// Store metric only when doing the final pass
|
||||||
|
if (loop_len == long_sb) {
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
simd_store(&betaPtr[8*k + i], old[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (loop_len!=long_sb) {
|
||||||
|
debug_state_beta(0);
|
||||||
|
} else {
|
||||||
|
debug_state_beta(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize
|
||||||
|
MAKE_FUNC(normalize)(k, old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Computes alpha metrics */
|
||||||
|
static void MAKE_FUNC(alpha)(MAKE_TYPE * s, llr_t *input, llr_t *app, llr_t *parity, llr_t * output, uint32_t long_cb)
|
||||||
|
{
|
||||||
|
simd_type_t m_b[8], new[8], old[8], max1[8], max0[8];
|
||||||
|
simd_type_t x, y, xy, ap;
|
||||||
|
simd_type_t m1, m0;
|
||||||
|
|
||||||
|
simd_type_t *inputPtr;
|
||||||
|
simd_type_t *appPtr;
|
||||||
|
simd_type_t *parityPtr;
|
||||||
|
simd_type_t *betaPtr = (simd_type_t*) s->beta;
|
||||||
|
simd_type_t *outputPtr = (simd_type_t*) output;
|
||||||
|
|
||||||
|
#if debug_enabled_win
|
||||||
|
simd_type_t beta_save[8];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Skip state 0
|
||||||
|
betaPtr+=8;
|
||||||
|
|
||||||
|
uint32_t loop_len;
|
||||||
|
|
||||||
|
for (int j=0;j<2;j++) {
|
||||||
|
|
||||||
|
// First run L states to find initial state for all sub-blocks after first
|
||||||
|
if (j==0) {
|
||||||
|
loop_len = win_overlap_len;
|
||||||
|
} else {
|
||||||
|
loop_len = long_sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When passing through all window pick estimated initial states (known state for sb=0)
|
||||||
|
if (loop_len == long_sb) {
|
||||||
|
|
||||||
|
#ifdef WINIMP_IS_AVX16
|
||||||
|
llr_t tmp[8];
|
||||||
|
for (int i=0;i<8;i++) {
|
||||||
|
tmp[i] = _mm256_extract_epi16(old[i], 7);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef WINIMP_IS_AVX8
|
||||||
|
llr_t tmp[8];
|
||||||
|
for (int i=0;i<8;i++) {
|
||||||
|
tmp[i] = _mm256_extract_epi8(old[i], 15);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
old[i] = simd_shuffle(old[i], move_left);
|
||||||
|
}
|
||||||
|
#ifdef WINIMP_IS_AVX16
|
||||||
|
for (int i=0;i<8;i++) {
|
||||||
|
old[i] = _mm256_insert_epi16(old[i], tmp[i], 8);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef WINIMP_IS_AVX8
|
||||||
|
for (int i=0;i<8;i++) {
|
||||||
|
old[i] = _mm256_insert_epi8(old[i], tmp[i], 16);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// 1st sub-block state is known
|
||||||
|
old[0] = simd_insert(old[0], 0, 0);
|
||||||
|
for (int i = 1; i < 8; i++) {
|
||||||
|
old[i] = simd_insert(old[i], -INF, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// when estimating states, just set all to unknown
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
old[i] = simd_set1(-INF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inputPtr = (simd_type_t*) &input[nof_blocks*(long_sb-loop_len)];
|
||||||
|
appPtr = (simd_type_t*) &app[nof_blocks*(long_sb-loop_len)];
|
||||||
|
parityPtr = (simd_type_t*) &parity[nof_blocks*(long_sb-loop_len)];
|
||||||
|
|
||||||
|
for (int k = 0; k < loop_len; k++) {
|
||||||
|
x = simd_load(inputPtr++);
|
||||||
|
y = simd_load(parityPtr++);
|
||||||
|
|
||||||
|
if (app) {
|
||||||
|
ap = simd_load(appPtr++);
|
||||||
|
x = simd_add(ap, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
xy = simd_add(x,y);
|
||||||
|
|
||||||
|
m_b[0] = old[0];
|
||||||
|
m_b[1] = simd_add(old[3], y);
|
||||||
|
m_b[2] = simd_add(old[4], y);
|
||||||
|
m_b[3] = old[7];
|
||||||
|
m_b[4] = old[1];
|
||||||
|
m_b[5] = simd_add(old[2], y);
|
||||||
|
m_b[6] = simd_add(old[5], y);
|
||||||
|
m_b[7] = old[6];
|
||||||
|
|
||||||
|
new[0] = simd_add(old[1], xy);
|
||||||
|
new[1] = simd_add(old[2], x);
|
||||||
|
new[2] = simd_add(old[5], x);
|
||||||
|
new[3] = simd_add(old[6], xy);
|
||||||
|
new[4] = simd_add(old[0], xy);
|
||||||
|
new[5] = simd_add(old[3], x);
|
||||||
|
new[6] = simd_add(old[4], x);
|
||||||
|
new[7] = simd_add(old[7], xy);
|
||||||
|
|
||||||
|
// Load beta and compute output only when passing through all window
|
||||||
|
if (loop_len == long_sb) {
|
||||||
|
simd_type_t beta;
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
beta = simd_load(betaPtr++);
|
||||||
|
max0[i] = simd_add(beta, m_b[i]);
|
||||||
|
max1[i] = simd_add(beta, new[i]);
|
||||||
|
|
||||||
|
#if debug_enabled_win
|
||||||
|
beta_save[i] = beta;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
m1 = simd_max(max1[0], max1[1]);
|
||||||
|
m0 = simd_max(max0[0], max0[1]);
|
||||||
|
|
||||||
|
for (int i = 2; i < 8; i++) {
|
||||||
|
m1 = simd_max(m1, max1[i]);
|
||||||
|
m0 = simd_max(m0, max0[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
simd_type_t out = simd_sub(m1, m0);
|
||||||
|
|
||||||
|
// Divide output when using 8-bit arithmetic
|
||||||
|
#ifdef divide_output
|
||||||
|
out = simd_rb_shift(out, divide_output);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
simd_store(outputPtr++, out);
|
||||||
|
|
||||||
|
debug_state(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
old[i] = simd_max(m_b[i], new[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize
|
||||||
|
MAKE_FUNC(normalize)(k, old);
|
||||||
|
|
||||||
|
if (loop_len != long_sb) {
|
||||||
|
debug_state_pre(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int MAKE_FUNC(init)(void **hh, uint32_t max_long_cb)
|
||||||
|
{
|
||||||
|
*hh = calloc(1, sizeof(MAKE_TYPE));
|
||||||
|
|
||||||
|
MAKE_TYPE *h = (MAKE_TYPE*) *hh;
|
||||||
|
|
||||||
|
h->beta = srslte_vec_malloc(sizeof(llr_t) * 8 * max_long_cb * nof_blocks);
|
||||||
|
if (!h->beta) {
|
||||||
|
perror("srslte_vec_malloc");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
h->max_long_cb = max_long_cb;
|
||||||
|
return nof_blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MAKE_FUNC(free)(void *hh)
|
||||||
|
{
|
||||||
|
MAKE_TYPE *h = (MAKE_TYPE*) hh;
|
||||||
|
if (h->beta) {
|
||||||
|
free(h->beta);
|
||||||
|
}
|
||||||
|
bzero(h, sizeof(MAKE_TYPE));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MAKE_FUNC(dec)(void *hh, llr_t *input, llr_t *app, llr_t *parity, llr_t *output, uint32_t long_cb)
|
||||||
|
{
|
||||||
|
MAKE_TYPE *h = (MAKE_TYPE*) hh;
|
||||||
|
MAKE_FUNC(beta)(h, input, app, parity, long_cb);
|
||||||
|
MAKE_FUNC(alpha)(h, input, app, parity, output, long_cb);
|
||||||
|
#if debug_enabled_win
|
||||||
|
printf("running win decoder: %s\n", STRING(WINIMP));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INSERT8_INPUT(reg, st, off) reg = simd_insert(reg, input[3*(i+(st+0)*long_sb)+off], st+0);\
|
||||||
|
reg = simd_insert(reg, input[3*(i+(st+1)*long_sb)+off], st+1);\
|
||||||
|
reg = simd_insert(reg, input[3*(i+(st+2)*long_sb)+off], st+2);\
|
||||||
|
reg = simd_insert(reg, input[3*(i+(st+3)*long_sb)+off], st+3);\
|
||||||
|
reg = simd_insert(reg, input[3*(i+(st+4)*long_sb)+off], st+4);\
|
||||||
|
reg = simd_insert(reg, input[3*(i+(st+5)*long_sb)+off], st+5);\
|
||||||
|
reg = simd_insert(reg, input[3*(i+(st+6)*long_sb)+off], st+6);\
|
||||||
|
reg = simd_insert(reg, input[3*(i+(st+7)*long_sb)+off], st+7);
|
||||||
|
|
||||||
|
|
||||||
|
void MAKE_FUNC(extract_input)(llr_t *input, llr_t *systematic, llr_t *app2, llr_t *parity_0, llr_t *parity_1, uint32_t long_cb)
|
||||||
|
{
|
||||||
|
simd_type_t *systPtr = (simd_type_t*) systematic;
|
||||||
|
simd_type_t *parity0Ptr = (simd_type_t*) parity_0;
|
||||||
|
simd_type_t *parity1Ptr = (simd_type_t*) parity_1;
|
||||||
|
|
||||||
|
simd_type_t syst, parity0, parity1;
|
||||||
|
|
||||||
|
for (int i=0;i<long_sb;i++) {
|
||||||
|
INSERT8_INPUT(syst, 0, 0);
|
||||||
|
INSERT8_INPUT(parity0, 0, 1);
|
||||||
|
INSERT8_INPUT(parity1, 0, 2);
|
||||||
|
|
||||||
|
#if nof_blocks >= 16
|
||||||
|
INSERT8_INPUT(syst, 8, 0);
|
||||||
|
INSERT8_INPUT(parity0, 8, 1);
|
||||||
|
INSERT8_INPUT(parity1, 8, 2);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if nof_blocks >= 32
|
||||||
|
INSERT8_INPUT(syst, 16, 0);
|
||||||
|
INSERT8_INPUT(parity0, 16, 1);
|
||||||
|
INSERT8_INPUT(parity1, 16, 2);
|
||||||
|
INSERT8_INPUT(syst, 24, 0);
|
||||||
|
INSERT8_INPUT(parity0, 24, 1);
|
||||||
|
INSERT8_INPUT(parity1, 24, 2);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
simd_store(systPtr++, syst);
|
||||||
|
simd_store(parity0Ptr++, parity0);
|
||||||
|
simd_store(parity1Ptr++, parity1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = long_cb; i < long_cb + 3; i++) {
|
||||||
|
systematic[i] = input[3*long_cb + 2*(i - long_cb)];
|
||||||
|
parity_0[i] = input[3*long_cb + 2*(i - long_cb) + 1];
|
||||||
|
|
||||||
|
app2[i] = input[3*long_cb + 6 + 2*(i - long_cb)];
|
||||||
|
parity_1[i] = input[3*long_cb + 6 + 2*(i - long_cb) + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define deinter(x,win) ((x%(long_cb/win))*(win)+x/(long_cb/win))
|
||||||
|
|
||||||
|
#define reset_cnt(a,b) if(!((a+1)%b)) { \
|
||||||
|
k+=b*nof_blocks; \
|
||||||
|
if (k >= long_cb) { \
|
||||||
|
k -= (long_cb-1);\
|
||||||
|
}\
|
||||||
|
}
|
||||||
|
#define insert_bit(a,b) ap = _mm_insert_epi16(ap, app1[k+(a%b)*nof_blocks], 7-a); \
|
||||||
|
reset_cnt(a,b); \
|
||||||
|
|
||||||
|
|
||||||
|
#define decide_for(b) for (uint32_t i = 0; i < long_cb/8; i++) { \
|
||||||
|
insert_bit(0,b);\
|
||||||
|
insert_bit(1,b);\
|
||||||
|
insert_bit(2,b);\
|
||||||
|
insert_bit(3,b);\
|
||||||
|
insert_bit(4,b);\
|
||||||
|
insert_bit(5,b);\
|
||||||
|
insert_bit(6,b);\
|
||||||
|
insert_bit(7,b);\
|
||||||
|
output[i] = (uint8_t) _mm_movemask_epi8(_mm_cmpgt_epi8(_mm_packs_epi16(ap,zeros),zeros));\
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No improvement to use AVX here */
|
||||||
|
void MAKE_FUNC(decision_byte)(llr_t *app1, uint8_t *output, uint32_t long_cb)
|
||||||
|
{
|
||||||
|
uint32_t k=0;
|
||||||
|
__m128i zeros = _mm_setzero_si128();
|
||||||
|
__m128i ap;
|
||||||
|
|
||||||
|
if ((long_cb%(nof_blocks*8)) == 0) {
|
||||||
|
decide_for(8);
|
||||||
|
} else if ((long_cb%(nof_blocks*4)) == 0) {
|
||||||
|
decide_for(4);
|
||||||
|
} else if ((long_cb%(nof_blocks*2)) == 0) {
|
||||||
|
decide_for(2);
|
||||||
|
} else {
|
||||||
|
decide_for(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#undef WINIMP
|
||||||
|
#undef nof_blocks
|
||||||
|
#undef llr_t
|
||||||
|
#undef normalize_period
|
||||||
|
#undef INF
|
||||||
|
#undef win_overlap_len
|
||||||
|
#undef simd_type_t
|
||||||
|
#undef simd_load
|
||||||
|
#undef simd_store
|
||||||
|
#undef simd_add
|
||||||
|
#undef simd_sub
|
||||||
|
#undef simd_max
|
||||||
|
#undef simd_set1
|
||||||
|
#undef simd_insert
|
||||||
|
#undef simd_shuffle
|
||||||
|
#undef move_right
|
||||||
|
#undef move_left
|
||||||
|
#undef debug_enabled_win
|
||||||
|
|
||||||
|
#ifdef normalize_max
|
||||||
|
#undef normalize_max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef use_saturated_add
|
||||||
|
#undef use_saturated_add
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef simd_rb_shift
|
||||||
|
#undef simd_rb_shift
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef divide_output
|
||||||
|
#undef divide_output
|
||||||
|
#endif
|
@ -1,475 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2015 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the srsLTE library.
|
|
||||||
*
|
|
||||||
* srsLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include "srslte/phy/fec/turbodecoder_simd.h"
|
|
||||||
#include "srslte/phy/utils/vector.h"
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#define NUMSTATES 8
|
|
||||||
#define NINPUTS 2
|
|
||||||
#define TAIL 3
|
|
||||||
#define TOTALTAIL 12
|
|
||||||
|
|
||||||
#define INF 10000
|
|
||||||
#define ZERO 0
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef LV_HAVE_AVX2
|
|
||||||
|
|
||||||
#include <smmintrin.h>
|
|
||||||
#include <immintrin.h>
|
|
||||||
|
|
||||||
|
|
||||||
// Number of CB processed in parllel in AVX
|
|
||||||
#define NCB 2
|
|
||||||
|
|
||||||
/*
|
|
||||||
static void print_256i(__m256i x) {
|
|
||||||
int16_t *s = (int16_t*) &x;
|
|
||||||
printf("[%d", s[0]);
|
|
||||||
for (int i=1;i<16;i++) {
|
|
||||||
printf(",%d", s[i]);
|
|
||||||
}
|
|
||||||
printf("]\n");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Computes the horizontal MAX from 8 16-bit integers using the minpos_epu16 SSE4.1 instruction */
|
|
||||||
static inline int16_t hMax0(__m256i masked_value)
|
|
||||||
{
|
|
||||||
__m128i tmp1 = _mm256_extractf128_si256(masked_value, 0);
|
|
||||||
__m128i tmp3 = _mm_minpos_epu16(tmp1);
|
|
||||||
return (int16_t)(_mm_cvtsi128_si32(tmp3));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int16_t hMax1(__m256i masked_value)
|
|
||||||
{
|
|
||||||
__m128i tmp1 = _mm256_extractf128_si256(masked_value, 1);
|
|
||||||
__m128i tmp3 = _mm_minpos_epu16(tmp1);
|
|
||||||
return (int16_t)(_mm_cvtsi128_si32(tmp3));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Computes beta values */
|
|
||||||
void map_avx_beta(map_gen_t * s, int16_t * output[SRSLTE_TDEC_MAX_NPAR], uint32_t long_cb)
|
|
||||||
{
|
|
||||||
int k;
|
|
||||||
uint32_t end = long_cb + 3;
|
|
||||||
const __m256i *alphaPtr = (const __m256i*) s->alpha;
|
|
||||||
|
|
||||||
__m256i beta_k = _mm256_set_epi16(-INF, -INF, -INF, -INF, -INF, -INF, -INF, 0, -INF, -INF, -INF, -INF, -INF, -INF, -INF, 0);
|
|
||||||
__m256i g, bp, bn, alpha_k;
|
|
||||||
|
|
||||||
/* Define the shuffle constant for the positive beta */
|
|
||||||
__m256i shuf_bp = _mm256_set_epi8(
|
|
||||||
// 1st CB
|
|
||||||
15+16, 14+16, // 7
|
|
||||||
7+16, 6+16, // 3
|
|
||||||
5+16, 4+16, // 2
|
|
||||||
13+16, 12+16, // 6
|
|
||||||
11+16, 10+16, // 5
|
|
||||||
3+16, 2+16, // 1
|
|
||||||
1+16, 0+16, // 0
|
|
||||||
9+16, 8+16, // 4
|
|
||||||
|
|
||||||
// 2nd CB
|
|
||||||
15, 14, // 7
|
|
||||||
7, 6, // 3
|
|
||||||
5, 4, // 2
|
|
||||||
13, 12, // 6
|
|
||||||
11, 10, // 5
|
|
||||||
3, 2, // 1
|
|
||||||
1, 0, // 0
|
|
||||||
9, 8 // 4
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Define the shuffle constant for the negative beta */
|
|
||||||
__m256i shuf_bn = _mm256_set_epi8(
|
|
||||||
7+16, 6+16, // 3
|
|
||||||
15+16, 14+16, // 7
|
|
||||||
13+16, 12+16, // 6
|
|
||||||
5+16, 4+16, // 2
|
|
||||||
3+16, 2+16, // 1
|
|
||||||
11+16, 10+16, // 5
|
|
||||||
9+16, 8+16, // 4
|
|
||||||
1+16, 0+16, // 0
|
|
||||||
|
|
||||||
7, 6, // 3
|
|
||||||
15, 14, // 7
|
|
||||||
13, 12, // 6
|
|
||||||
5, 4, // 2
|
|
||||||
3, 2, // 1
|
|
||||||
11, 10, // 5
|
|
||||||
9, 8, // 4
|
|
||||||
1, 0 // 0
|
|
||||||
);
|
|
||||||
|
|
||||||
alphaPtr += long_cb-1;
|
|
||||||
|
|
||||||
/* Define shuffle for branch costs */
|
|
||||||
__m256i shuf_g[4];
|
|
||||||
shuf_g[3] = _mm256_set_epi8(3+16,2+16,1+16,0+16,1+16,0+16,3+16,2+16,3+16,2+16,1+16,0+16,1+16,0+16,3+16,2+16,
|
|
||||||
3,2,1,0,1,0,3,2,3,2,1,0,1,0,3,2);
|
|
||||||
shuf_g[2] = _mm256_set_epi8(7+16,6+16,5+16,4+16,5+16,4+16,7+16,6+16,7+16,6+16,5+16,4+16,5+16,4+16,7+16,6+16,
|
|
||||||
7,6,5,4,5,4,7,6,7,6,5,4,5,4,7,6);
|
|
||||||
shuf_g[1] = _mm256_set_epi8(11+16,10+16,9+16,8+16,9+16,8+16,11+16,10+16,11+16,10+16,9+16,8+16,9+16,8+16,11+16,10+16,
|
|
||||||
11,10,9,8,9,8,11,10,11,10,9,8,9,8,11,10);
|
|
||||||
shuf_g[0] = _mm256_set_epi8(15+16,14+16,13+16,12+16,13+16,12+16,15+16,14+16,15+16,14+16,13+16,12+16,13+16,12+16,15+16,14+16,
|
|
||||||
15,14,13,12,13,12,15,14,15,14,13,12,13,12,15,14);
|
|
||||||
|
|
||||||
/* Define shuffle for beta normalization */
|
|
||||||
__m256i shuf_norm = _mm256_set_epi8(17,16,17,16,17,16,17,16,17,16,17,16,17,16,17,16,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0);
|
|
||||||
|
|
||||||
__m256i gv;
|
|
||||||
int16_t *b = &s->branch[2*NCB*long_cb-16];
|
|
||||||
__m256i *gPtr = (__m256i*) b;
|
|
||||||
|
|
||||||
/* This defines a beta computation step:
|
|
||||||
* Adds and substracts the branch metrics to the previous beta step,
|
|
||||||
* shuffles the states according to the trellis path and selects maximum state
|
|
||||||
*/
|
|
||||||
#define BETA_STEP(g) bp = _mm256_add_epi16(beta_k, g);\
|
|
||||||
bn = _mm256_sub_epi16(beta_k, g);\
|
|
||||||
bp = _mm256_shuffle_epi8(bp, shuf_bp);\
|
|
||||||
bn = _mm256_shuffle_epi8(bn, shuf_bn);\
|
|
||||||
beta_k = _mm256_max_epi16(bp, bn);
|
|
||||||
|
|
||||||
/* Loads the alpha metrics from memory and adds them to the temporal bn and bp
|
|
||||||
* metrics. Then computes horizontal maximum of both metrics and computes difference
|
|
||||||
*/
|
|
||||||
#define BETA_STEP_CNT(c,d) g = _mm256_shuffle_epi8(gv, shuf_g[c]);\
|
|
||||||
BETA_STEP(g)\
|
|
||||||
alpha_k = _mm256_load_si256(alphaPtr);\
|
|
||||||
alphaPtr--;\
|
|
||||||
bp = _mm256_add_epi16(bp, alpha_k);\
|
|
||||||
bn = _mm256_add_epi16(bn, alpha_k);\
|
|
||||||
bn = _mm256_sub_epi16(_mm256_set1_epi16(0x7FFF), bn);\
|
|
||||||
bp = _mm256_sub_epi16(_mm256_set1_epi16(0x7FFF), bp);\
|
|
||||||
output[0][k-d] = hMax0(bn) - hMax0(bp);\
|
|
||||||
output[1][k-d] = hMax1(bn) - hMax1(bp);
|
|
||||||
|
|
||||||
/* The tail does not require to load alpha or produce outputs. Only update
|
|
||||||
* beta metrics accordingly */
|
|
||||||
for (k=end-1; k>=long_cb; k--) {
|
|
||||||
int16_t g0_1 = s->branch[2*NCB*k];
|
|
||||||
int16_t g1_1 = s->branch[2*NCB*k+1];
|
|
||||||
int16_t g0_2 = s->branch[2*NCB*k+6];
|
|
||||||
int16_t g1_2 = s->branch[2*NCB*k+6+1];
|
|
||||||
g = _mm256_set_epi16(g1_2, g0_2, g0_2, g1_2, g1_2, g0_2, g0_2, g1_2, g1_1, g0_1, g0_1, g1_1, g1_1, g0_1, g0_1, g1_1);
|
|
||||||
BETA_STEP(g);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We inline 2 trelis steps for each normalization */
|
|
||||||
__m256i norm;
|
|
||||||
for (; k >= 0; k-=8) {
|
|
||||||
gv = _mm256_load_si256(gPtr);
|
|
||||||
gPtr--;
|
|
||||||
BETA_STEP_CNT(0,0);
|
|
||||||
BETA_STEP_CNT(1,1);
|
|
||||||
BETA_STEP_CNT(2,2);
|
|
||||||
BETA_STEP_CNT(3,3);
|
|
||||||
norm = _mm256_shuffle_epi8(beta_k, shuf_norm);
|
|
||||||
beta_k = _mm256_sub_epi16(beta_k, norm);
|
|
||||||
gv = _mm256_load_si256(gPtr);
|
|
||||||
gPtr--;
|
|
||||||
BETA_STEP_CNT(0,4);
|
|
||||||
BETA_STEP_CNT(1,5);
|
|
||||||
BETA_STEP_CNT(2,6);
|
|
||||||
BETA_STEP_CNT(3,7);
|
|
||||||
norm = _mm256_shuffle_epi8(beta_k, shuf_norm);
|
|
||||||
beta_k = _mm256_sub_epi16(beta_k, norm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Computes alpha metrics */
|
|
||||||
void map_avx_alpha(map_gen_t * s, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
uint32_t k;
|
|
||||||
int16_t *alpha1 = s->alpha;
|
|
||||||
int16_t *alpha2 = &s->alpha[8];
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
alpha1[0] = 0;
|
|
||||||
alpha2[0] = 0;
|
|
||||||
for (i = 1; i < 8; i++) {
|
|
||||||
alpha1[i] = -INF;
|
|
||||||
alpha2[i] = -INF;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Define the shuffle constant for the positive alpha */
|
|
||||||
__m256i shuf_ap = _mm256_set_epi8(
|
|
||||||
|
|
||||||
// 1st CB
|
|
||||||
31, 30, // 7
|
|
||||||
25, 24, // 4
|
|
||||||
23, 22, // 3
|
|
||||||
17, 16, // 0
|
|
||||||
29, 28, // 6
|
|
||||||
27, 26, // 5
|
|
||||||
21, 20, // 2
|
|
||||||
19, 18, // 1
|
|
||||||
|
|
||||||
// 2nd CB
|
|
||||||
15, 14, // 7
|
|
||||||
9, 8, // 4
|
|
||||||
7, 6, // 3
|
|
||||||
1, 0, // 0
|
|
||||||
13, 12, // 6
|
|
||||||
11, 10, // 5
|
|
||||||
5, 4, // 2
|
|
||||||
3, 2 // 1
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Define the shuffle constant for the negative alpha */
|
|
||||||
__m256i shuf_an = _mm256_set_epi8(
|
|
||||||
|
|
||||||
// 1nd CB
|
|
||||||
29, 28, // 6
|
|
||||||
27, 26, // 5
|
|
||||||
21, 20, // 2
|
|
||||||
19, 18, // 1
|
|
||||||
31, 30, // 7
|
|
||||||
25, 24, // 4
|
|
||||||
23, 22, // 3
|
|
||||||
17, 16, // 0
|
|
||||||
|
|
||||||
// 2nd CB
|
|
||||||
13, 12, // 6
|
|
||||||
11, 10, // 5
|
|
||||||
5, 4, // 2
|
|
||||||
3, 2, // 1
|
|
||||||
15, 14, // 7
|
|
||||||
9, 8, // 4
|
|
||||||
7, 6, // 3
|
|
||||||
1, 0 // 0
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Define shuffle for branch costs */
|
|
||||||
__m256i shuf_g[4];
|
|
||||||
shuf_g[0] = _mm256_set_epi8(3+16,2+16,3+16,2+16,1+16,0+16,1+16,0+16,1+16,0+16,1+16,0+16,3+16,2+16,3+16,2+16,
|
|
||||||
3,2,3,2,1,0,1,0,1,0,1,0,3,2,3,2);
|
|
||||||
shuf_g[1] = _mm256_set_epi8(7+16,6+16,7+16,6+16,5+16,4+16,5+16,4+16,5+16,4+16,5+16,4+16,7+16,6+16,7+16,6+16,
|
|
||||||
7,6,7,6,5,4,5,4,5,4,5,4,7,6,7,6);
|
|
||||||
shuf_g[2] = _mm256_set_epi8(11+16,10+16,11+16,10+16,9+16,8+16,9+16,8+16,9+16,8+16,9+16,8+16,11+16,10+16,11+16,10+16,
|
|
||||||
11,10,11,10,9,8,9,8,9,8,9,8,11,10,11,10);
|
|
||||||
shuf_g[3] = _mm256_set_epi8(15+16,14+16,15+16,14+16,13+16,12+16,13+16,12+16,13+16,12+16,13+16,12+16,15+16,14+16,15+16,14+16,
|
|
||||||
15,14,15,14,13,12,13,12,13,12,13,12,15,14,15,14);
|
|
||||||
|
|
||||||
__m256i shuf_norm = _mm256_set_epi8(17,16,17,16,17,16,17,16,17,16,17,16,17,16,17,16,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0);
|
|
||||||
|
|
||||||
__m256i* alphaPtr = (__m256i*) s->alpha;
|
|
||||||
alphaPtr++;
|
|
||||||
|
|
||||||
__m256i gv;
|
|
||||||
__m256i *gPtr = (__m256i*) s->branch;
|
|
||||||
__m256i g, ap, an;
|
|
||||||
|
|
||||||
__m256i alpha_k = _mm256_set_epi16(-INF, -INF, -INF, -INF, -INF, -INF, -INF, 0, -INF, -INF, -INF, -INF, -INF, -INF, -INF, 0);
|
|
||||||
|
|
||||||
/* This defines a alpha computation step:
|
|
||||||
* Adds and substracts the branch metrics to the previous alpha step,
|
|
||||||
* shuffles the states according to the trellis path and selects maximum state
|
|
||||||
*/
|
|
||||||
#define ALPHA_STEP(c) g = _mm256_shuffle_epi8(gv, shuf_g[c]); \
|
|
||||||
ap = _mm256_add_epi16(alpha_k, g);\
|
|
||||||
an = _mm256_sub_epi16(alpha_k, g);\
|
|
||||||
ap = _mm256_shuffle_epi8(ap, shuf_ap);\
|
|
||||||
an = _mm256_shuffle_epi8(an, shuf_an);\
|
|
||||||
alpha_k = _mm256_max_epi16(ap, an);\
|
|
||||||
_mm256_store_si256(alphaPtr, alpha_k);\
|
|
||||||
alphaPtr++;\
|
|
||||||
|
|
||||||
|
|
||||||
/* In this loop, we compute 8 steps and normalize twice for each branch metrics memory load */
|
|
||||||
__m256i norm;
|
|
||||||
for (k = 0; k < long_cb/8; k++) {
|
|
||||||
gv = _mm256_load_si256(gPtr);
|
|
||||||
|
|
||||||
gPtr++;
|
|
||||||
ALPHA_STEP(0);
|
|
||||||
ALPHA_STEP(1);
|
|
||||||
ALPHA_STEP(2);
|
|
||||||
ALPHA_STEP(3);
|
|
||||||
norm = _mm256_shuffle_epi8(alpha_k, shuf_norm);
|
|
||||||
alpha_k = _mm256_sub_epi16(alpha_k, norm);
|
|
||||||
gv = _mm256_load_si256(gPtr);
|
|
||||||
gPtr++;
|
|
||||||
ALPHA_STEP(0);
|
|
||||||
ALPHA_STEP(1);
|
|
||||||
ALPHA_STEP(2);
|
|
||||||
ALPHA_STEP(3);
|
|
||||||
norm = _mm256_shuffle_epi8(alpha_k, shuf_norm);
|
|
||||||
alpha_k = _mm256_sub_epi16(alpha_k, norm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void map_sse_gamma_single(int16_t *output, int16_t *input, int16_t *app, int16_t *parity)
|
|
||||||
{
|
|
||||||
__m128i res00, res10, res01, res11, res0, res1;
|
|
||||||
__m128i in, ap, pa, g1, g0;
|
|
||||||
|
|
||||||
__m128i *inPtr = (__m128i*) input;
|
|
||||||
__m128i *appPtr = (__m128i*) app;
|
|
||||||
__m128i *paPtr = (__m128i*) parity;
|
|
||||||
__m128i *resPtr = (__m128i*) output;
|
|
||||||
|
|
||||||
__m128i res00_mask = _mm_set_epi8(0xff,0xff,7,6,0xff,0xff,5,4,0xff,0xff,3,2,0xff,0xff,1,0);
|
|
||||||
__m128i res10_mask = _mm_set_epi8(0xff,0xff,15,14,0xff,0xff,13,12,0xff,0xff,11,10,0xff,0xff,9,8);
|
|
||||||
__m128i res01_mask = _mm_set_epi8(7,6,0xff,0xff,5,4,0xff,0xff,3,2,0xff,0xff,1,0,0xff,0xff);
|
|
||||||
__m128i res11_mask = _mm_set_epi8(15,14,0xff,0xff,13,12,0xff,0xff,11,10,0xff,0xff,9,8,0xff,0xff);
|
|
||||||
|
|
||||||
in = _mm_load_si128(inPtr);
|
|
||||||
inPtr++;
|
|
||||||
pa = _mm_load_si128(paPtr);
|
|
||||||
paPtr++;
|
|
||||||
|
|
||||||
if (appPtr) {
|
|
||||||
ap = _mm_load_si128(appPtr);
|
|
||||||
appPtr++;
|
|
||||||
in = _mm_add_epi16(ap, in);
|
|
||||||
}
|
|
||||||
|
|
||||||
g1 = _mm_add_epi16(in, pa);
|
|
||||||
g0 = _mm_sub_epi16(in, pa);
|
|
||||||
|
|
||||||
g1 = _mm_srai_epi16(g1, 1);
|
|
||||||
g0 = _mm_srai_epi16(g0, 1);
|
|
||||||
|
|
||||||
res00 = _mm_shuffle_epi8(g0, res00_mask);
|
|
||||||
res10 = _mm_shuffle_epi8(g0, res10_mask);
|
|
||||||
res01 = _mm_shuffle_epi8(g1, res01_mask);
|
|
||||||
res11 = _mm_shuffle_epi8(g1, res11_mask);
|
|
||||||
|
|
||||||
res0 = _mm_or_si128(res00, res01);
|
|
||||||
res1 = _mm_or_si128(res10, res11);
|
|
||||||
|
|
||||||
_mm_store_si128(resPtr, res0);
|
|
||||||
resPtr++;
|
|
||||||
_mm_store_si128(resPtr, res1);
|
|
||||||
resPtr++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Compute branch metrics (gamma) */
|
|
||||||
void map_avx_gamma(map_gen_t * h, int16_t *input, int16_t *app, int16_t *parity, uint32_t cbidx, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
__m128i res10, res20, res11, res21, res1, res2;
|
|
||||||
__m256i in, ap, pa, g1, g0;
|
|
||||||
|
|
||||||
__m256i *inPtr = (__m256i*) input;
|
|
||||||
__m256i *appPtr = (__m256i*) app;
|
|
||||||
__m256i *paPtr = (__m256i*) parity;
|
|
||||||
__m128i *resPtr = (__m128i*) h->branch;
|
|
||||||
|
|
||||||
if (cbidx) {
|
|
||||||
resPtr++;
|
|
||||||
}
|
|
||||||
|
|
||||||
__m128i res10_mask = _mm_set_epi8(0xff,0xff,7,6,0xff,0xff,5,4,0xff,0xff,3,2,0xff,0xff,1,0);
|
|
||||||
__m128i res11_mask = _mm_set_epi8(7,6,0xff,0xff,5,4,0xff,0xff,3,2,0xff,0xff,1,0,0xff,0xff);
|
|
||||||
|
|
||||||
__m128i res20_mask = _mm_set_epi8(0xff,0xff,15,14,0xff,0xff,13,12,0xff,0xff,11,10,0xff,0xff,9,8);
|
|
||||||
__m128i res21_mask = _mm_set_epi8(15,14,0xff,0xff,13,12,0xff,0xff,11,10,0xff,0xff,9,8,0xff,0xff);
|
|
||||||
|
|
||||||
for (int i=0;i<long_cb/16;i++) {
|
|
||||||
in = _mm256_load_si256(inPtr);
|
|
||||||
inPtr++;
|
|
||||||
pa = _mm256_load_si256(paPtr);
|
|
||||||
paPtr++;
|
|
||||||
|
|
||||||
if (appPtr) {
|
|
||||||
ap = _mm256_load_si256(appPtr);
|
|
||||||
appPtr++;
|
|
||||||
in = _mm256_add_epi16(ap, in);
|
|
||||||
}
|
|
||||||
|
|
||||||
g0 = _mm256_sub_epi16(in, pa);
|
|
||||||
g1 = _mm256_add_epi16(in, pa);
|
|
||||||
|
|
||||||
g0 = _mm256_srai_epi16(g0, 1);
|
|
||||||
g1 = _mm256_srai_epi16(g1, 1);
|
|
||||||
|
|
||||||
__m128i g0_t = _mm256_extractf128_si256(g0, 0);
|
|
||||||
__m128i g1_t = _mm256_extractf128_si256(g1, 0);
|
|
||||||
|
|
||||||
res10 = _mm_shuffle_epi8(g0_t, res10_mask);
|
|
||||||
res11 = _mm_shuffle_epi8(g1_t, res11_mask);
|
|
||||||
|
|
||||||
res20 = _mm_shuffle_epi8(g0_t, res20_mask);
|
|
||||||
res21 = _mm_shuffle_epi8(g1_t, res21_mask);
|
|
||||||
|
|
||||||
res1 = _mm_or_si128(res10, res11);
|
|
||||||
res2 = _mm_or_si128(res20, res21);
|
|
||||||
|
|
||||||
_mm_store_si128(resPtr, res1);
|
|
||||||
resPtr++;
|
|
||||||
resPtr++;
|
|
||||||
_mm_store_si128(resPtr, res2);
|
|
||||||
resPtr++;
|
|
||||||
resPtr++;
|
|
||||||
|
|
||||||
g0_t = _mm256_extractf128_si256(g0, 1);
|
|
||||||
g1_t = _mm256_extractf128_si256(g1, 1);
|
|
||||||
|
|
||||||
res10 = _mm_shuffle_epi8(g0_t, res10_mask);
|
|
||||||
res11 = _mm_shuffle_epi8(g1_t, res11_mask);
|
|
||||||
|
|
||||||
res20 = _mm_shuffle_epi8(g0_t, res20_mask);
|
|
||||||
res21 = _mm_shuffle_epi8(g1_t, res21_mask);
|
|
||||||
|
|
||||||
res1 = _mm_or_si128(res10, res11);
|
|
||||||
res2 = _mm_or_si128(res20, res21);
|
|
||||||
|
|
||||||
_mm_store_si128(resPtr, res1);
|
|
||||||
resPtr++;
|
|
||||||
resPtr++;
|
|
||||||
_mm_store_si128(resPtr, res2);
|
|
||||||
resPtr++;
|
|
||||||
resPtr++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (long_cb%16) {
|
|
||||||
map_sse_gamma_single((int16_t*) resPtr, (int16_t*) inPtr, (int16_t*) appPtr, (int16_t*) paPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=long_cb;i<long_cb+3;i++) {
|
|
||||||
h->branch[2*i*NCB+cbidx*6] = (input[i] - parity[i])/2;
|
|
||||||
h->branch[2*i*NCB+cbidx*6+1] = (input[i] + parity[i])/2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
@ -1,542 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2015 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the srsLTE library.
|
|
||||||
*
|
|
||||||
* srsLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include "srslte/phy/fec/turbodecoder_simd.h"
|
|
||||||
#include "srslte/phy/utils/vector.h"
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#define NUMSTATES 8
|
|
||||||
#define NINPUTS 2
|
|
||||||
#define TAIL 3
|
|
||||||
#define TOTALTAIL 12
|
|
||||||
|
|
||||||
#define INF 10000
|
|
||||||
#define ZERO 0
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef LV_HAVE_SSE
|
|
||||||
#include <smmintrin.h>
|
|
||||||
|
|
||||||
// Define SSE/AVX implementations
|
|
||||||
void map_sse_beta(map_gen_t * s, int16_t * output, uint32_t long_cb);
|
|
||||||
void map_sse_alpha(map_gen_t * s, uint32_t long_cb);
|
|
||||||
void map_sse_gamma(map_gen_t * h, int16_t *input, int16_t *app, int16_t *parity, uint32_t long_cb);
|
|
||||||
|
|
||||||
#ifdef LV_HAVE_AVX2
|
|
||||||
void map_avx_beta(map_gen_t * s, int16_t * output[SRSLTE_TDEC_MAX_NPAR], uint32_t long_cb);
|
|
||||||
void map_avx_alpha(map_gen_t * s, uint32_t long_cb);
|
|
||||||
void map_avx_gamma(map_gen_t * h, int16_t *input, int16_t *app, int16_t *parity, uint32_t cbidx, uint32_t long_cb);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
void map_simd_beta(map_gen_t * s, int16_t * output[SRSLTE_TDEC_MAX_NPAR], uint32_t nof_cb, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
if (nof_cb == 1) {
|
|
||||||
map_sse_beta(s, output[0], long_cb);
|
|
||||||
}
|
|
||||||
#ifdef LV_HAVE_AVX2
|
|
||||||
else if (nof_cb == 2) {
|
|
||||||
map_avx_beta(s, output, long_cb);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void map_simd_alpha(map_gen_t * s, uint32_t nof_cb, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
if (nof_cb == 1) {
|
|
||||||
map_sse_alpha(s, long_cb);
|
|
||||||
}
|
|
||||||
#ifdef LV_HAVE_AVX2
|
|
||||||
else if (nof_cb == 2) {
|
|
||||||
map_avx_alpha(s, long_cb);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
void map_simd_gamma(map_gen_t * s, int16_t *input, int16_t *app, int16_t *parity, uint32_t cbidx, uint32_t nof_cb, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
if (nof_cb == 1) {
|
|
||||||
map_sse_gamma(s, input, app, parity, long_cb);
|
|
||||||
}
|
|
||||||
#ifdef LV_HAVE_AVX2
|
|
||||||
else if (nof_cb == 2) {
|
|
||||||
map_avx_gamma(s, input, app, parity, cbidx, long_cb);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inititalizes constituent decoder object */
|
|
||||||
int map_simd_init(map_gen_t * h, uint32_t max_par_cb, uint32_t max_long_cb)
|
|
||||||
{
|
|
||||||
bzero(h, sizeof(map_gen_t));
|
|
||||||
|
|
||||||
h->max_par_cb = max_par_cb;
|
|
||||||
h->max_long_cb = max_long_cb;
|
|
||||||
|
|
||||||
h->alpha = srslte_vec_malloc(sizeof(int16_t) * (max_long_cb + SRSLTE_TCOD_TOTALTAIL + 1) * NUMSTATES * h->max_par_cb);
|
|
||||||
if (!h->alpha) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
h->branch = srslte_vec_malloc(sizeof(int16_t) * (max_long_cb + SRSLTE_TCOD_TOTALTAIL + 1) * NUMSTATES * h->max_par_cb);
|
|
||||||
if (!h->branch) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void map_simd_free(map_gen_t * h)
|
|
||||||
{
|
|
||||||
if (h->alpha) {
|
|
||||||
free(h->alpha);
|
|
||||||
}
|
|
||||||
if (h->branch) {
|
|
||||||
free(h->branch);
|
|
||||||
}
|
|
||||||
bzero(h, sizeof(map_gen_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Runs one instance of a decoder */
|
|
||||||
void map_simd_dec(map_gen_t * h, int16_t * input[SRSLTE_TDEC_MAX_NPAR], int16_t *app[SRSLTE_TDEC_MAX_NPAR], int16_t * parity[SRSLTE_TDEC_MAX_NPAR],
|
|
||||||
int16_t *output[SRSLTE_TDEC_MAX_NPAR], uint32_t cb_mask, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
|
|
||||||
uint32_t nof_cb = 1;
|
|
||||||
int16_t *outptr[SRSLTE_TDEC_MAX_NPAR] = { NULL, NULL };
|
|
||||||
|
|
||||||
// Compute branch metrics
|
|
||||||
switch(cb_mask) {
|
|
||||||
case 1:
|
|
||||||
nof_cb = 1;
|
|
||||||
outptr[0] = output[0];
|
|
||||||
map_simd_gamma(h, input[0], app?app[0]:NULL, parity[0], 0, 1, long_cb);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
nof_cb = 1;
|
|
||||||
outptr[0] = output[1];
|
|
||||||
map_simd_gamma(h, input[1], app?app[1]:NULL, parity[1], 0, 1, long_cb);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
nof_cb = 2;
|
|
||||||
for (int i=0;i<2;i++) {
|
|
||||||
outptr[i] = output[i];
|
|
||||||
map_simd_gamma(h, input[i], app?app[i]:NULL, parity[i], i, 2, long_cb);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forward recursion
|
|
||||||
map_simd_alpha(h, nof_cb, long_cb);
|
|
||||||
|
|
||||||
// Backwards recursion + LLR computation
|
|
||||||
map_simd_beta(h, outptr, nof_cb, long_cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initializes the turbo decoder object */
|
|
||||||
int srslte_tdec_simd_init(srslte_tdec_simd_t * h, uint32_t max_par_cb, uint32_t max_long_cb)
|
|
||||||
{
|
|
||||||
int ret = -1;
|
|
||||||
bzero(h, sizeof(srslte_tdec_simd_t));
|
|
||||||
uint32_t len = max_long_cb + SRSLTE_TCOD_TOTALTAIL;
|
|
||||||
|
|
||||||
h->max_long_cb = max_long_cb;
|
|
||||||
h->max_par_cb = max_par_cb;
|
|
||||||
|
|
||||||
for (int i=0;i<h->max_par_cb;i++) {
|
|
||||||
h->app1[i] = srslte_vec_malloc(sizeof(int16_t) * len);
|
|
||||||
if (!h->app1[i]) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
h->app2[i] = srslte_vec_malloc(sizeof(int16_t) * len);
|
|
||||||
if (!h->app2[i]) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
h->ext1[i] = srslte_vec_malloc(sizeof(int16_t) * len);
|
|
||||||
if (!h->ext1[i]) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
h->ext2[i] = srslte_vec_malloc(sizeof(int16_t) * len);
|
|
||||||
if (!h->ext2[i]) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
h->syst[i] = srslte_vec_malloc(sizeof(int16_t) * len);
|
|
||||||
if (!h->syst[i]) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
h->parity0[i] = srslte_vec_malloc(sizeof(int16_t) * len);
|
|
||||||
if (!h->parity0[i]) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
h->parity1[i] = srslte_vec_malloc(sizeof(int16_t) * len);
|
|
||||||
if (!h->parity1[i]) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map_simd_init(&h->dec, h->max_par_cb, h->max_long_cb)) {
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0;i<SRSLTE_NOF_TC_CB_SIZES;i++) {
|
|
||||||
if (srslte_tc_interl_init(&h->interleaver[i], srslte_cbsegm_cbsize(i)) < 0) {
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
srslte_tc_interl_LTE_gen(&h->interleaver[i], srslte_cbsegm_cbsize(i));
|
|
||||||
}
|
|
||||||
h->current_cbidx = -1;
|
|
||||||
h->cb_mask = 0;
|
|
||||||
ret = 0;
|
|
||||||
clean_and_exit:if (ret == -1) {
|
|
||||||
srslte_tdec_simd_free(h);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void srslte_tdec_simd_free(srslte_tdec_simd_t * h)
|
|
||||||
{
|
|
||||||
for (int i=0;i<h->max_par_cb;i++) {
|
|
||||||
if (h->app1[i]) {
|
|
||||||
free(h->app1[i]);
|
|
||||||
}
|
|
||||||
if (h->app2[i]) {
|
|
||||||
free(h->app2[i]);
|
|
||||||
}
|
|
||||||
if (h->ext1[i]) {
|
|
||||||
free(h->ext1[i]);
|
|
||||||
}
|
|
||||||
if (h->ext2[i]) {
|
|
||||||
free(h->ext2[i]);
|
|
||||||
}
|
|
||||||
if (h->syst[i]) {
|
|
||||||
free(h->syst[i]);
|
|
||||||
}
|
|
||||||
if (h->parity0[i]) {
|
|
||||||
free(h->parity0[i]);
|
|
||||||
}
|
|
||||||
if (h->parity1[i]) {
|
|
||||||
free(h->parity1[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
map_simd_free(&h->dec);
|
|
||||||
|
|
||||||
for (int i=0;i<SRSLTE_NOF_TC_CB_SIZES;i++) {
|
|
||||||
srslte_tc_interl_free(&h->interleaver[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bzero(h, sizeof(srslte_tdec_simd_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Deinterleaves the 3 streams from the input (systematic and 2 parity bits) into
|
|
||||||
* 3 buffers ready to be used by compute_gamma()
|
|
||||||
*/
|
|
||||||
void deinterleave_input_simd(srslte_tdec_simd_t *h, int16_t *input, uint32_t cbidx, uint32_t long_cb) {
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
__m128i *inputPtr = (__m128i*) input;
|
|
||||||
__m128i in0, in1, in2;
|
|
||||||
__m128i s0, s1, s2, s;
|
|
||||||
__m128i p00, p01, p02, p0;
|
|
||||||
__m128i p10, p11, p12, p1;
|
|
||||||
|
|
||||||
__m128i *sysPtr = (__m128i*) h->syst[cbidx];
|
|
||||||
__m128i *pa0Ptr = (__m128i*) h->parity0[cbidx];
|
|
||||||
__m128i *pa1Ptr = (__m128i*) h->parity1[cbidx];
|
|
||||||
|
|
||||||
// pick bits 0, 3, 6 from 1st word
|
|
||||||
__m128i s0_mask = _mm_set_epi8(0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,13,12,7,6,1,0);
|
|
||||||
// pick bits 1, 4, 7 from 2st word
|
|
||||||
__m128i s1_mask = _mm_set_epi8(0xff,0xff,0xff,0xff,15,14,9,8,3,2,0xff,0xff,0xff,0xff,0xff,0xff);
|
|
||||||
// pick bits 2, 5 from 3rd word
|
|
||||||
__m128i s2_mask = _mm_set_epi8(11,10,5,4,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff);
|
|
||||||
|
|
||||||
// pick bits 1, 4, 7 from 1st word
|
|
||||||
__m128i p00_mask = _mm_set_epi8(0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,15,14,9,8,3,2);
|
|
||||||
// pick bits 2, 5, from 2st word
|
|
||||||
__m128i p01_mask = _mm_set_epi8(0xff,0xff,0xff,0xff,0xff,0xff,11,10,5,4,0xff,0xff,0xff,0xff,0xff,0xff);
|
|
||||||
// pick bits 0, 3, 6 from 3rd word
|
|
||||||
__m128i p02_mask = _mm_set_epi8(13,12,7,6,1,0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff);
|
|
||||||
|
|
||||||
// pick bits 2, 5 from 1st word
|
|
||||||
__m128i p10_mask = _mm_set_epi8(0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,11,10,5,4);
|
|
||||||
// pick bits 0, 3, 6, from 2st word
|
|
||||||
__m128i p11_mask = _mm_set_epi8(0xff,0xff,0xff,0xff,0xff,0xff,13,12,7,6,1,0,0xff,0xff,0xff,0xff);
|
|
||||||
// pick bits 1, 4, 7 from 3rd word
|
|
||||||
__m128i p12_mask = _mm_set_epi8(15,14,9,8,3,2,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff);
|
|
||||||
|
|
||||||
// Split systematic and parity bits
|
|
||||||
for (i = 0; i < long_cb/8; i++) {
|
|
||||||
|
|
||||||
in0 = _mm_load_si128(inputPtr); inputPtr++;
|
|
||||||
in1 = _mm_load_si128(inputPtr); inputPtr++;
|
|
||||||
in2 = _mm_load_si128(inputPtr); inputPtr++;
|
|
||||||
|
|
||||||
/* Deinterleave Systematic bits */
|
|
||||||
s0 = _mm_shuffle_epi8(in0, s0_mask);
|
|
||||||
s1 = _mm_shuffle_epi8(in1, s1_mask);
|
|
||||||
s2 = _mm_shuffle_epi8(in2, s2_mask);
|
|
||||||
s = _mm_or_si128(s0, s1);
|
|
||||||
s = _mm_or_si128(s, s2);
|
|
||||||
|
|
||||||
_mm_store_si128(sysPtr, s);
|
|
||||||
sysPtr++;
|
|
||||||
|
|
||||||
/* Deinterleave parity 0 bits */
|
|
||||||
p00 = _mm_shuffle_epi8(in0, p00_mask);
|
|
||||||
p01 = _mm_shuffle_epi8(in1, p01_mask);
|
|
||||||
p02 = _mm_shuffle_epi8(in2, p02_mask);
|
|
||||||
p0 = _mm_or_si128(p00, p01);
|
|
||||||
p0 = _mm_or_si128(p0, p02);
|
|
||||||
|
|
||||||
_mm_store_si128(pa0Ptr, p0);
|
|
||||||
pa0Ptr++;
|
|
||||||
|
|
||||||
/* Deinterleave parity 1 bits */
|
|
||||||
p10 = _mm_shuffle_epi8(in0, p10_mask);
|
|
||||||
p11 = _mm_shuffle_epi8(in1, p11_mask);
|
|
||||||
p12 = _mm_shuffle_epi8(in2, p12_mask);
|
|
||||||
p1 = _mm_or_si128(p10, p11);
|
|
||||||
p1 = _mm_or_si128(p1, p12);
|
|
||||||
|
|
||||||
_mm_store_si128(pa1Ptr, p1);
|
|
||||||
pa1Ptr++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 3; i++) {
|
|
||||||
h->syst[cbidx][i+long_cb] = input[3*long_cb + 2*i];
|
|
||||||
h->parity0[cbidx][i+long_cb] = input[3*long_cb + 2*i + 1];
|
|
||||||
}
|
|
||||||
for (i = 0; i < 3; i++) {
|
|
||||||
h->app2[cbidx][i+long_cb] = input[3*long_cb + 6 + 2*i];
|
|
||||||
h->parity1[cbidx][i+long_cb] = input[3*long_cb + 6 + 2*i + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Runs 1 turbo decoder iteration */
|
|
||||||
void srslte_tdec_simd_iteration(srslte_tdec_simd_t * h, int16_t * input[SRSLTE_TDEC_MAX_NPAR], uint32_t long_cb)
|
|
||||||
{
|
|
||||||
|
|
||||||
int16_t *tmp_app[SRSLTE_TDEC_MAX_NPAR];
|
|
||||||
|
|
||||||
if (h->current_cbidx >= 0) {
|
|
||||||
uint16_t *inter = h->interleaver[h->current_cbidx].forward;
|
|
||||||
uint16_t *deinter = h->interleaver[h->current_cbidx].reverse;
|
|
||||||
|
|
||||||
#ifndef LV_HAVE_AVX2
|
|
||||||
input[1] = NULL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
h->cb_mask = (input[0]?1:0) | (input[1]?2:0);
|
|
||||||
|
|
||||||
for (int i=0;i<h->max_par_cb;i++) {
|
|
||||||
if (h->n_iter[i] == 0 && input[i]) {
|
|
||||||
//printf("deinterleaveing %d\n",i);
|
|
||||||
deinterleave_input_simd(h, input[i], i, long_cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add apriori information to decoder 1
|
|
||||||
for (int i=0;i<h->max_par_cb;i++) {
|
|
||||||
if (h->n_iter[i] > 0 && input[i]) {
|
|
||||||
srslte_vec_sub_sss(h->app1[i], h->ext1[i], h->app1[i], long_cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run MAP DEC #1
|
|
||||||
for (int i=0;i<h->max_par_cb;i++) {
|
|
||||||
if (input[i]) {
|
|
||||||
tmp_app[i] = h->n_iter[i]?h->app1[i]:NULL;
|
|
||||||
} else {
|
|
||||||
tmp_app[i] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
map_simd_dec(&h->dec, h->syst, tmp_app, h->parity0, h->ext1, h->cb_mask, long_cb);
|
|
||||||
|
|
||||||
// Convert aposteriori information into extrinsic information
|
|
||||||
for (int i=0;i<h->max_par_cb;i++) {
|
|
||||||
if (h->n_iter[i] > 0 && input[i]) {
|
|
||||||
srslte_vec_sub_sss(h->ext1[i], h->app1[i], h->ext1[i], long_cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interleave extrinsic output of DEC1 to form apriori info for decoder 2
|
|
||||||
for (int i=0;i<h->max_par_cb;i++) {
|
|
||||||
if (input[i]) {
|
|
||||||
srslte_vec_lut_sss(h->ext1[i], deinter, h->app2[i], long_cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run MAP DEC #2. 2nd decoder uses apriori information as systematic bits
|
|
||||||
map_simd_dec(&h->dec, h->app2, NULL, h->parity1, h->ext2, h->cb_mask, long_cb);
|
|
||||||
|
|
||||||
// Deinterleaved extrinsic bits become apriori info for decoder 1
|
|
||||||
for (int i=0;i<h->max_par_cb;i++) {
|
|
||||||
if (input[i]) {
|
|
||||||
srslte_vec_lut_sss(h->ext2[i], inter, h->app1[i], long_cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0;i<h->max_par_cb;i++) {
|
|
||||||
if (input[i]) {
|
|
||||||
h->n_iter[i]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Error CB index not set (call srslte_tdec_simd_reset() first\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resets the decoder and sets the codeblock length */
|
|
||||||
int srslte_tdec_simd_reset(srslte_tdec_simd_t * h, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
if (long_cb > h->max_long_cb) {
|
|
||||||
fprintf(stderr, "TDEC was initialized for max_long_cb=%d\n",
|
|
||||||
h->max_long_cb);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
for (int i=0;i<h->max_par_cb;i++) {
|
|
||||||
h->n_iter[i] = 0;
|
|
||||||
}
|
|
||||||
h->cb_mask = 0;
|
|
||||||
h->current_cbidx = srslte_cbsegm_cbindex(long_cb);
|
|
||||||
if (h->current_cbidx < 0) {
|
|
||||||
fprintf(stderr, "Invalid CB length %d\n", long_cb);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int srslte_tdec_simd_reset_cb(srslte_tdec_simd_t * h, uint32_t cb_idx)
|
|
||||||
{
|
|
||||||
h->n_iter[cb_idx] = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int srslte_tdec_simd_get_nof_iterations_cb(srslte_tdec_simd_t * h, uint32_t cb_idx)
|
|
||||||
{
|
|
||||||
return h->n_iter[cb_idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
void tdec_simd_decision(srslte_tdec_simd_t * h, uint8_t *output, uint32_t cbidx, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
__m128i zero = _mm_set1_epi16(0);
|
|
||||||
__m128i lsb_mask = _mm_set1_epi16(1);
|
|
||||||
|
|
||||||
__m128i *appPtr = (__m128i*) h->app1[cbidx];
|
|
||||||
__m128i *outPtr = (__m128i*) output;
|
|
||||||
__m128i ap, out, out0, out1;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < long_cb/16; i++) {
|
|
||||||
ap = _mm_load_si128(appPtr); appPtr++;
|
|
||||||
out0 = _mm_and_si128(_mm_cmpgt_epi16(ap, zero), lsb_mask);
|
|
||||||
ap = _mm_load_si128(appPtr); appPtr++;
|
|
||||||
out1 = _mm_and_si128(_mm_cmpgt_epi16(ap, zero), lsb_mask);
|
|
||||||
|
|
||||||
out = _mm_packs_epi16(out0, out1);
|
|
||||||
_mm_store_si128(outPtr, out);
|
|
||||||
outPtr++;
|
|
||||||
}
|
|
||||||
if (long_cb%16) {
|
|
||||||
for (int i=0;i<8;i++) {
|
|
||||||
output[long_cb-8+i] = h->app1[cbidx][long_cb-8+i]>0?1:0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void srslte_tdec_simd_decision(srslte_tdec_simd_t * h, uint8_t *output[SRSLTE_TDEC_MAX_NPAR], uint32_t long_cb)
|
|
||||||
{
|
|
||||||
for (int i=0;i<h->max_par_cb;i++) {
|
|
||||||
tdec_simd_decision(h, output[i], i, long_cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void srslte_tdec_simd_decision_byte_cb(srslte_tdec_simd_t * h, uint8_t *output, uint32_t cbidx, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
uint8_t mask[8] = {0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1};
|
|
||||||
|
|
||||||
// long_cb is always byte aligned
|
|
||||||
for (uint32_t i = 0; i < long_cb/8; i++) {
|
|
||||||
uint8_t out0 = h->app1[cbidx][8*i+0]>0?mask[0]:0;
|
|
||||||
uint8_t out1 = h->app1[cbidx][8*i+1]>0?mask[1]:0;
|
|
||||||
uint8_t out2 = h->app1[cbidx][8*i+2]>0?mask[2]:0;
|
|
||||||
uint8_t out3 = h->app1[cbidx][8*i+3]>0?mask[3]:0;
|
|
||||||
uint8_t out4 = h->app1[cbidx][8*i+4]>0?mask[4]:0;
|
|
||||||
uint8_t out5 = h->app1[cbidx][8*i+5]>0?mask[5]:0;
|
|
||||||
uint8_t out6 = h->app1[cbidx][8*i+6]>0?mask[6]:0;
|
|
||||||
uint8_t out7 = h->app1[cbidx][8*i+7]>0?mask[7]:0;
|
|
||||||
|
|
||||||
output[i] = out0 | out1 | out2 | out3 | out4 | out5 | out6 | out7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void srslte_tdec_simd_decision_byte(srslte_tdec_simd_t * h, uint8_t *output[SRSLTE_TDEC_MAX_NPAR], uint32_t long_cb)
|
|
||||||
{
|
|
||||||
for (int i=0;i<h->max_par_cb;i++) {
|
|
||||||
if (output[i]) {
|
|
||||||
srslte_tdec_simd_decision_byte_cb(h, output[i], i, long_cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Runs nof_iterations iterations and decides the output bits */
|
|
||||||
int srslte_tdec_simd_run_all(srslte_tdec_simd_t * h, int16_t * input[SRSLTE_TDEC_MAX_NPAR], uint8_t *output[SRSLTE_TDEC_MAX_NPAR],
|
|
||||||
uint32_t nof_iterations, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
if (srslte_tdec_simd_reset(h, long_cb)) {
|
|
||||||
return SRSLTE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
srslte_tdec_simd_iteration(h, input, long_cb);
|
|
||||||
} while (h->n_iter[0] < nof_iterations);
|
|
||||||
|
|
||||||
srslte_tdec_simd_decision_byte(h, output, long_cb);
|
|
||||||
|
|
||||||
return SRSLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
@ -1,299 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2015 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the srsLTE library.
|
|
||||||
*
|
|
||||||
* srsLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include "srslte/phy/fec/turbodecoder_simd_inter.h"
|
|
||||||
#include "srslte/phy/utils/vector.h"
|
|
||||||
|
|
||||||
#define TOTALTAIL 12
|
|
||||||
|
|
||||||
#ifdef LV_HAVE_SSE
|
|
||||||
#include <smmintrin.h>
|
|
||||||
|
|
||||||
void map_see_inter_alpha(srslte_tdec_simd_inter_t * s, int16_t *input, int16_t *parity, uint32_t long_cb);
|
|
||||||
void map_sse_inter_beta(srslte_tdec_simd_inter_t * s, int16_t *input, int16_t *parity, int16_t * output, uint32_t long_cb);
|
|
||||||
void sse_inter_update_w(srslte_tdec_simd_inter_t *h, uint16_t *deinter, uint32_t long_cb);
|
|
||||||
void sse_inter_extract_syst1(srslte_tdec_simd_inter_t *h, uint16_t *inter, uint32_t long_cb);
|
|
||||||
|
|
||||||
|
|
||||||
static void map_sse_inter_dec(srslte_tdec_simd_inter_t * h, int16_t * input, int16_t * parity, int16_t * output,
|
|
||||||
uint32_t long_cb)
|
|
||||||
{
|
|
||||||
map_see_inter_alpha(h, input, parity, long_cb);
|
|
||||||
map_sse_inter_beta(h, input, parity, output, long_cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************************
|
|
||||||
*
|
|
||||||
* TURBO DECODER INTERFACE
|
|
||||||
*
|
|
||||||
************************************************/
|
|
||||||
int srslte_tdec_simd_inter_init(srslte_tdec_simd_inter_t * h, uint32_t max_par_cb, uint32_t max_long_cb)
|
|
||||||
{
|
|
||||||
int ret = -1;
|
|
||||||
bzero(h, sizeof(srslte_tdec_simd_inter_t));
|
|
||||||
uint32_t len = max_long_cb + 12;
|
|
||||||
|
|
||||||
h->max_long_cb = max_long_cb;
|
|
||||||
h->max_par_cb = max_par_cb;
|
|
||||||
|
|
||||||
h->llr1 = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb);
|
|
||||||
if (!h->llr1) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
h->llr2 = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb);
|
|
||||||
if (!h->llr2) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
h->w = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb);
|
|
||||||
if (!h->w) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
h->syst0 = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb);
|
|
||||||
if (!h->syst0) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
h->syst1 = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb);
|
|
||||||
if (!h->syst1) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
h->parity0 = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb);
|
|
||||||
if (!h->parity0) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
h->parity1 = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb);
|
|
||||||
if (!h->parity1) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
h->alpha = srslte_vec_malloc(sizeof(int16_t) * 8*(len+12) * h->max_par_cb);
|
|
||||||
if (!h->alpha) {
|
|
||||||
perror("srslte_vec_malloc");
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0;i<SRSLTE_NOF_TC_CB_SIZES;i++) {
|
|
||||||
if (srslte_tc_interl_init(&h->interleaver[i], srslte_cbsegm_cbsize(i)) < 0) {
|
|
||||||
goto clean_and_exit;
|
|
||||||
}
|
|
||||||
srslte_tc_interl_LTE_gen(&h->interleaver[i], srslte_cbsegm_cbsize(i));
|
|
||||||
}
|
|
||||||
h->current_cbidx = -1;
|
|
||||||
ret = 0;
|
|
||||||
clean_and_exit:if (ret == -1) {
|
|
||||||
srslte_tdec_simd_inter_free(h);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void srslte_tdec_simd_inter_free(srslte_tdec_simd_inter_t * h)
|
|
||||||
{
|
|
||||||
if (h->llr1) {
|
|
||||||
free(h->llr1);
|
|
||||||
}
|
|
||||||
if (h->llr2) {
|
|
||||||
free(h->llr2);
|
|
||||||
}
|
|
||||||
if (h->w) {
|
|
||||||
free(h->w);
|
|
||||||
}
|
|
||||||
if (h->syst0) {
|
|
||||||
free(h->syst0);
|
|
||||||
}
|
|
||||||
if (h->syst1) {
|
|
||||||
free(h->syst1);
|
|
||||||
}
|
|
||||||
if (h->parity0) {
|
|
||||||
free(h->parity0);
|
|
||||||
}
|
|
||||||
if (h->parity1) {
|
|
||||||
free(h->parity1);
|
|
||||||
}
|
|
||||||
if (h->alpha) {
|
|
||||||
free(h->alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0;i<SRSLTE_NOF_TC_CB_SIZES;i++) {
|
|
||||||
srslte_tc_interl_free(&h->interleaver[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bzero(h, sizeof(srslte_tdec_simd_inter_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Deinterleave for inter-frame parallelization */
|
|
||||||
void extract_input(srslte_tdec_simd_inter_t *h, int16_t *input, uint32_t cbidx, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
for (int i=0;i<long_cb;i++) {
|
|
||||||
h->syst0[h->max_par_cb*i+cbidx] = input[3*i+0];
|
|
||||||
h->parity0[h->max_par_cb*i+cbidx] = input[3*i+1];
|
|
||||||
h->parity1[h->max_par_cb*i+cbidx] = input[3*i+2];
|
|
||||||
}
|
|
||||||
for (int i = long_cb; i < long_cb + 3; i++) {
|
|
||||||
h->syst0[h->max_par_cb*i+cbidx] = input[3*long_cb + 2*(i - long_cb)];
|
|
||||||
h->syst1[h->max_par_cb*i+cbidx] = input[3*long_cb + 2*(i - long_cb)];
|
|
||||||
h->parity0[h->max_par_cb*i+cbidx] = input[3*long_cb + 2*(i - long_cb) + 1];
|
|
||||||
h->parity0[h->max_par_cb*i+cbidx] = input[3*long_cb + 2*(i - long_cb) + 2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void srslte_tdec_simd_inter_iteration(srslte_tdec_simd_inter_t * h, int16_t *input[SRSLTE_TDEC_MAX_NPAR], uint32_t nof_cb, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (h->current_cbidx >= 0) {
|
|
||||||
|
|
||||||
uint16_t *inter = h->interleaver[h->current_cbidx].forward;
|
|
||||||
uint16_t *deinter = h->interleaver[h->current_cbidx].reverse;
|
|
||||||
|
|
||||||
// Prepare systematic and parity bits for MAP DEC #1
|
|
||||||
for (int i=0;i<nof_cb;i++) {
|
|
||||||
if (h->n_iter[i] == 0) {
|
|
||||||
extract_input(h, input[i], i, long_cb);
|
|
||||||
}
|
|
||||||
srslte_vec_sum_sss(h->syst0, h->w, h->syst0, long_cb*h->max_par_cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run MAP DEC #1
|
|
||||||
map_sse_inter_dec(h, h->syst0, h->parity0, h->llr1, long_cb);
|
|
||||||
|
|
||||||
// Prepare systematic and parity bits for MAP DEC #1
|
|
||||||
sse_inter_extract_syst1(h, inter, long_cb);
|
|
||||||
|
|
||||||
// Run MAP DEC #2
|
|
||||||
map_sse_inter_dec(h, h->syst1, h->parity1, h->llr2, long_cb);
|
|
||||||
|
|
||||||
// Update a-priori LLR from the last iteration
|
|
||||||
sse_inter_update_w(h, deinter, long_cb);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Error CB index not set (call srslte_tdec_simd_inter_reset() first\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int srslte_tdec_simd_inter_reset_cb(srslte_tdec_simd_inter_t * h, uint32_t cb_idx)
|
|
||||||
{
|
|
||||||
for (int i=0;i<h->current_long_cb;i++) {
|
|
||||||
h->w[h->max_par_cb*i+cb_idx] = 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int srslte_tdec_simd_inter_reset(srslte_tdec_simd_inter_t * h, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
if (long_cb > h->max_long_cb) {
|
|
||||||
fprintf(stderr, "TDEC was initialized for max_long_cb=%d\n",
|
|
||||||
h->max_long_cb);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
h->current_long_cb = long_cb;
|
|
||||||
h->current_cbidx = srslte_cbsegm_cbindex(long_cb);
|
|
||||||
if (h->current_cbidx < 0) {
|
|
||||||
fprintf(stderr, "Invalid CB length %d\n", long_cb);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memset(h->w, 0, sizeof(int16_t) * long_cb * h->max_par_cb);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void srslte_tdec_simd_inter_decision_cb(srslte_tdec_simd_inter_t * h, uint8_t *output, uint32_t cb_idx, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
uint16_t *deinter = h->interleaver[h->current_cbidx].reverse;
|
|
||||||
uint32_t i;
|
|
||||||
for (i = 0; i < long_cb; i++) {
|
|
||||||
output[i] = (h->llr2[h->max_par_cb*deinter[i]+cb_idx] > 0) ? 1 : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void srslte_tdec_simd_inter_decision(srslte_tdec_simd_inter_t * h, uint8_t *output[SRSLTE_TDEC_MAX_NPAR], uint32_t nof_cb, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
for (int i=0;i<nof_cb;i++) {
|
|
||||||
srslte_tdec_simd_inter_decision_cb(h, output[i], i, long_cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void srslte_tdec_simd_inter_decision_byte_cb(srslte_tdec_simd_inter_t * h, uint8_t *output, uint32_t cb_idx, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
uint32_t i;
|
|
||||||
uint8_t mask[8] = {0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1};
|
|
||||||
uint16_t *deinter = h->interleaver[h->current_cbidx].reverse;
|
|
||||||
|
|
||||||
#define indexOf_cb(idx, cb) (h->max_par_cb*(deinter[8*i+idx])+cb)
|
|
||||||
|
|
||||||
// long_cb is always byte aligned
|
|
||||||
for (i = 0; i < long_cb/8; i++) {
|
|
||||||
uint8_t out0 = h->llr2[indexOf_cb(0, cb_idx)]>0?mask[0]:0;
|
|
||||||
uint8_t out1 = h->llr2[indexOf_cb(1, cb_idx)]>0?mask[1]:0;
|
|
||||||
uint8_t out2 = h->llr2[indexOf_cb(2, cb_idx)]>0?mask[2]:0;
|
|
||||||
uint8_t out3 = h->llr2[indexOf_cb(3, cb_idx)]>0?mask[3]:0;
|
|
||||||
uint8_t out4 = h->llr2[indexOf_cb(4, cb_idx)]>0?mask[4]:0;
|
|
||||||
uint8_t out5 = h->llr2[indexOf_cb(5, cb_idx)]>0?mask[5]:0;
|
|
||||||
uint8_t out6 = h->llr2[indexOf_cb(6, cb_idx)]>0?mask[6]:0;
|
|
||||||
uint8_t out7 = h->llr2[indexOf_cb(7, cb_idx)]>0?mask[7]:0;
|
|
||||||
|
|
||||||
output[i] = out0 | out1 | out2 | out3 | out4 | out5 | out6 | out7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void srslte_tdec_simd_inter_decision_byte(srslte_tdec_simd_inter_t * h, uint8_t *output[SRSLTE_TDEC_MAX_NPAR], uint32_t nof_cb, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
for (int i=0;i<nof_cb;i++) {
|
|
||||||
srslte_tdec_simd_inter_decision_byte_cb(h, output[i], i, long_cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int srslte_tdec_simd_inter_run_all(srslte_tdec_simd_inter_t * h,
|
|
||||||
int16_t *input[SRSLTE_TDEC_MAX_NPAR], uint8_t *output[SRSLTE_TDEC_MAX_NPAR],
|
|
||||||
uint32_t nof_iterations, uint32_t nof_cb, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
uint32_t iter = 0;
|
|
||||||
|
|
||||||
if (srslte_tdec_simd_inter_reset(h, long_cb)) {
|
|
||||||
return SRSLTE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
srslte_tdec_simd_inter_iteration(h, input, nof_cb, long_cb);
|
|
||||||
iter++;
|
|
||||||
} while (iter < nof_iterations);
|
|
||||||
|
|
||||||
srslte_tdec_simd_inter_decision_byte(h, output, nof_cb, long_cb);
|
|
||||||
|
|
||||||
return SRSLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,202 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2015 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the srsLTE library.
|
|
||||||
*
|
|
||||||
* srsLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include "srslte/phy/fec/turbodecoder_simd_inter.h"
|
|
||||||
#include "srslte/phy/utils/vector.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define NCB 8
|
|
||||||
|
|
||||||
#define INF 10000
|
|
||||||
|
|
||||||
#ifdef LV_HAVE_SSE
|
|
||||||
#include <smmintrin.h>
|
|
||||||
|
|
||||||
void sse_inter_extract_syst1(srslte_tdec_simd_inter_t *h, uint16_t *inter, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
__m128i *llr1Ptr = (__m128i*) h->llr1;
|
|
||||||
__m128i *wPtr = (__m128i*) h->w;
|
|
||||||
__m128i *syst1Ptr = (__m128i*) h->syst1;
|
|
||||||
|
|
||||||
for (int i = 0; i < long_cb; i++) {
|
|
||||||
__m128i llr1 = _mm_load_si128(&llr1Ptr[inter[i]]);
|
|
||||||
__m128i w = _mm_load_si128(&wPtr[inter[i]]);
|
|
||||||
_mm_store_si128(syst1Ptr++, _mm_sub_epi16(llr1, w));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sse_inter_update_w(srslte_tdec_simd_inter_t *h, uint16_t *deinter, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
__m128i *llr1Ptr = (__m128i*) h->llr1;
|
|
||||||
__m128i *llr2Ptr = (__m128i*) h->llr2;
|
|
||||||
__m128i *wPtr = (__m128i*) h->w;
|
|
||||||
__m128i *syst1Ptr = (__m128i*) h->syst1;
|
|
||||||
|
|
||||||
for (int i = 0; i < long_cb; i++) {
|
|
||||||
__m128i llr1 = _mm_load_si128(llr1Ptr++);
|
|
||||||
__m128i w = _mm_load_si128(wPtr++);
|
|
||||||
__m128i llr2 = _mm_load_si128(&llr2Ptr[deinter[i]]);
|
|
||||||
|
|
||||||
_mm_store_si128(syst1Ptr++, _mm_add_epi16(w, _mm_sub_epi16(llr2, llr1)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Computes beta values */
|
|
||||||
void map_sse_inter_beta(srslte_tdec_simd_inter_t * s, int16_t *input, int16_t *parity, int16_t * output, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
__m128i m_b[8], new[8], old[8], max1[8], max0[8];
|
|
||||||
__m128i x, y, xy;
|
|
||||||
__m128i m1, m0;
|
|
||||||
uint32_t end = long_cb + 3;
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
__m128i *inputPtr = (__m128i*) input;
|
|
||||||
__m128i *parityPtr = (__m128i*) parity;
|
|
||||||
__m128i *outputPtr = (__m128i*) output;
|
|
||||||
__m128i *alphaPtr = (__m128i*) s->alpha;
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
old[i] = _mm_set1_epi16(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int k = end - 1; k >= 0; k--) {
|
|
||||||
x = _mm_load_si128(inputPtr++);
|
|
||||||
y = _mm_load_si128(parityPtr++);
|
|
||||||
|
|
||||||
xy = _mm_add_epi16(x,y);
|
|
||||||
|
|
||||||
m_b[0] = _mm_add_epi16(old[4], xy);
|
|
||||||
m_b[1] = old[4];
|
|
||||||
m_b[2] = _mm_add_epi16(old[5], y);
|
|
||||||
m_b[3] = _mm_add_epi16(old[5], x);
|
|
||||||
m_b[4] = _mm_add_epi16(old[6], x);
|
|
||||||
m_b[5] = _mm_add_epi16(old[6], y);
|
|
||||||
m_b[6] = old[7];
|
|
||||||
m_b[7] = _mm_add_epi16(old[7], xy);
|
|
||||||
|
|
||||||
new[0] = old[0];
|
|
||||||
new[1] = _mm_add_epi16(old[0], xy);
|
|
||||||
new[2] = _mm_add_epi16(old[1], x);
|
|
||||||
new[3] = _mm_add_epi16(old[1], y);
|
|
||||||
new[4] = _mm_add_epi16(old[2], y);
|
|
||||||
new[5] = _mm_add_epi16(old[2], x);
|
|
||||||
new[6] = _mm_add_epi16(old[3], xy);
|
|
||||||
new[7] = old[3];
|
|
||||||
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
__m128i alpha = _mm_load_si128(alphaPtr++);
|
|
||||||
max0[i] = _mm_add_epi16(alpha, m_b[i]);
|
|
||||||
max1[i] = _mm_add_epi16(alpha, new[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
m1 = _mm_max_epi16(max1[0], max1[1]);
|
|
||||||
m0 = _mm_max_epi16(max0[0], max0[1]);
|
|
||||||
|
|
||||||
for (i = 2; i < 8; i++) {
|
|
||||||
m1 = _mm_max_epi16(m1, max1[i]);
|
|
||||||
m0 = _mm_max_epi16(m0, max0[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
new[i] = _mm_max_epi16(m_b[i], new[i]);
|
|
||||||
old[i] = new[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
__m128i out = _mm_sub_epi16(m1, m0);
|
|
||||||
_mm_store_si128(outputPtr++, out);
|
|
||||||
|
|
||||||
// normalize
|
|
||||||
if ((k%4)==0) {
|
|
||||||
for (int i=1;i<8;i++) {
|
|
||||||
_mm_sub_epi16(old[i], old[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Computes alpha metrics */
|
|
||||||
void map_see_inter_alpha(srslte_tdec_simd_inter_t * s, int16_t *input, int16_t *parity, uint32_t long_cb)
|
|
||||||
{
|
|
||||||
__m128i m_b[8], new[8], old[8];
|
|
||||||
__m128i x, y, xy;
|
|
||||||
uint32_t k;
|
|
||||||
|
|
||||||
__m128i *inputPtr = (__m128i*) input;
|
|
||||||
__m128i *parityPtr = (__m128i*) parity;
|
|
||||||
__m128i *alphaPtr = (__m128i*) s->alpha;
|
|
||||||
|
|
||||||
old[0] = _mm_set1_epi16(0);
|
|
||||||
for (int i = 1; i < 8; i++) {
|
|
||||||
old[i] = _mm_set1_epi16(-INF);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (k = 0; k < long_cb; k++) {
|
|
||||||
x = _mm_load_si128(inputPtr++);
|
|
||||||
y = _mm_load_si128(parityPtr++);
|
|
||||||
|
|
||||||
xy = _mm_add_epi16(x,y);
|
|
||||||
|
|
||||||
m_b[0] = old[0];
|
|
||||||
m_b[1] = _mm_add_epi16(old[3], y);
|
|
||||||
m_b[2] = _mm_add_epi16(old[4], y);
|
|
||||||
m_b[3] = old[7];
|
|
||||||
m_b[4] = old[1];
|
|
||||||
m_b[5] = _mm_add_epi16(old[2], y);
|
|
||||||
m_b[6] = _mm_add_epi16(old[5], y);
|
|
||||||
m_b[7] = old[6];
|
|
||||||
|
|
||||||
new[0] = _mm_add_epi16(old[1], xy);
|
|
||||||
new[1] = _mm_add_epi16(old[2], x);
|
|
||||||
new[2] = _mm_add_epi16(old[5], x);
|
|
||||||
new[3] = _mm_add_epi16(old[6], xy);
|
|
||||||
new[4] = _mm_add_epi16(old[0], xy);
|
|
||||||
new[5] = _mm_add_epi16(old[3], x);
|
|
||||||
new[6] = _mm_add_epi16(old[4], x);
|
|
||||||
new[7] = _mm_add_epi16(old[7], xy);
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
new[i] = _mm_max_epi16(m_b[i], new[i]);
|
|
||||||
old[i] = new[i];
|
|
||||||
_mm_store_si128(alphaPtr++, old[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize
|
|
||||||
if ((k%4)==0) {
|
|
||||||
for (int i=1;i<8;i++) {
|
|
||||||
_mm_sub_epi16(old[i], old[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue