mirror of https://github.com/pvnis/srsRAN_4G.git
Merging next into NAS cleanup.
commit
8dc029cc47
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsUE library.
|
||||||
|
*
|
||||||
|
* srsUE 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.
|
||||||
|
*
|
||||||
|
* srsUE 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_INT_HELPERS_H
|
||||||
|
#define SRSLTE_INT_HELPERS_H
|
||||||
|
|
||||||
|
namespace srslte {
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* Safe conversions between byte buffers and integer types.
|
||||||
|
* Note: these don't perform endian conversion - use e.g. htonl/ntohl if required
|
||||||
|
*****************************************************************************/
|
||||||
|
inline void uint8_to_uint32(uint8_t *buf, uint32_t *i)
|
||||||
|
{
|
||||||
|
*i = (uint32_t)buf[0] << 24 |
|
||||||
|
(uint32_t)buf[1] << 16 |
|
||||||
|
(uint32_t)buf[2] << 8 |
|
||||||
|
(uint32_t)buf[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void uint32_to_uint8(uint32_t i, uint8_t *buf)
|
||||||
|
{
|
||||||
|
buf[0] = (i >> 24) & 0xFF;
|
||||||
|
buf[1] = (i >> 16) & 0xFF;
|
||||||
|
buf[2] = (i >> 8) & 0xFF;
|
||||||
|
buf[3] = i & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void uint8_to_uint16(uint8_t *buf, uint16_t *i)
|
||||||
|
{
|
||||||
|
*i = (uint32_t)buf[0] << 8 |
|
||||||
|
(uint32_t)buf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void uint16_to_uint8(uint16_t i, uint8_t *buf)
|
||||||
|
{
|
||||||
|
buf[0] = (i >> 8) & 0xFF;
|
||||||
|
buf[1] = i & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; //namespace
|
||||||
|
|
||||||
|
#endif // SRSLTE_INT_HELPERS_H
|
@ -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,752 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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_AVX2
|
||||||
|
#error "Selected AVX2 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_AVX2
|
||||||
|
#error "Selected AVX2 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) {
|
||||||
|
if (h->beta) {
|
||||||
|
free(h->beta);
|
||||||
|
}
|
||||||
|
free(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue