/** * * \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 #include #include #include #include #include #include "srslte/fec/rm_turbo.h" #include "srslte/utils/bit.h" #include "srslte/utils/vector.h" #include "srslte/fec/cbsegm.h" #ifdef LV_HAVE_SSE #include int srslte_rm_turbo_rx_lut_sse(int16_t *input, int16_t *output, uint32_t in_len, uint32_t cb_idx, uint32_t rv_idx); #endif #ifdef LV_HAVE_AVX #include int srslte_rm_turbo_rx_lut_avx(int16_t *input, int16_t *output, uint32_t in_len, uint32_t cb_idx, uint32_t rv_idx); #endif #define NCOLS 32 #define NROWS_MAX NCOLS static uint8_t RM_PERM_TC[NCOLS] = { 0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31 }; /* Align tables to 16-byte boundary */ static uint16_t interleaver_systematic_bits[192][6160]; // 4 tail bits static uint16_t interleaver_parity_bits[192][2*6160]; static uint16_t deinterleaver[192][4][18448]; static int k0_vec[SRSLTE_NOF_TC_CB_SIZES][4][2]; static bool rm_turbo_tables_generated = false; static uint16_t temp_table1[3*6176], temp_table2[3*6176]; void srslte_rm_turbo_gentable_systematic(uint16_t *table_bits, int k0_vec[4][2], uint32_t nrows, int ndummy) { bool last_is_null=true; int k_b=0, buff_idx=0; for (int j = 0; j < NCOLS; j++) { for (int i = 0; i < nrows; i++) { if (i * NCOLS + RM_PERM_TC[j] >= ndummy) { table_bits[k_b] = i * NCOLS + RM_PERM_TC[j] - ndummy; k_b++; last_is_null=false; } else { last_is_null=true; } for (int i=0;i<4;i++) { if (k0_vec[i][1] == -1) { if (k0_vec[i][0]%(3*nrows*NCOLS) <= buff_idx && !last_is_null) { k0_vec[i][1] = k_b-1; } } } buff_idx++; } } } void srslte_rm_turbo_gentable_parity(uint16_t *table_parity, int k0_vec[4][2], int offset, uint16_t nrows, int ndummy) { bool last_is_null=true; int k_b=0, buff_idx0=0; int K_p = nrows*NCOLS; int buff_idx1=0; for (int j = 0; j < NCOLS; j++) { for (int i = 0; i < nrows; i++) { if (i * NCOLS + RM_PERM_TC[j] >= ndummy) { table_parity[k_b] = i * NCOLS + RM_PERM_TC[j] - ndummy; k_b++; last_is_null=false; } else { last_is_null=true; } for (int i=0;i<4;i++) { if (k0_vec[i][1] == -1) { if (k0_vec[i][0]%(3*K_p) <= 2*buff_idx0+K_p && !last_is_null) { k0_vec[i][1] = offset+k_b-1; } } } buff_idx0++; int kidx = (RM_PERM_TC[buff_idx1 / nrows] + NCOLS * (buff_idx1 % nrows) + 1) % K_p; if ((kidx - ndummy) >= 0) { table_parity[k_b] = kidx-ndummy+offset; k_b++; last_is_null=false; } else { last_is_null=true; } for (int i=0;i<4;i++) { if (k0_vec[i][1] == -1) { if (k0_vec[i][0]%(3*K_p) <= 2*buff_idx1+1+K_p && !last_is_null) { k0_vec[i][1] = offset+k_b-1; } } } buff_idx1++; } } } void srslte_rm_turbo_gentable_receive(uint16_t *table, uint32_t cb_len, uint32_t rv_idx) { int nrows = (uint32_t) (cb_len / 3 - 1) / NCOLS + 1; int ndummy = nrows*NCOLS - cb_len / 3; if (ndummy < 0) { ndummy = 0; } /* Undo bit collection. Account for dummy bits */ int N_cb = 3*nrows*NCOLS; int k0 = nrows*(2*(uint16_t) ceilf((float) N_cb/(float) (8*nrows))*rv_idx+2); int kidx; int K_p = nrows * NCOLS; int k = 0, jp=0, j=0; bool isdummy = false; int d_i, d_j; while (k < cb_len) { jp = (k0 + j) % N_cb; if (jp < K_p || !(jp % 2)) { if (jp >= K_p) { d_i = ((jp - K_p) / 2) / nrows; d_j = ((jp - K_p) / 2) % nrows; } else { d_i = jp / nrows; d_j = jp % nrows; } if (d_j * NCOLS + RM_PERM_TC[d_i] >= ndummy) { isdummy = false; if (d_j * NCOLS + RM_PERM_TC[d_i] - ndummy < 0) { isdummy = true; } } else { isdummy = true; } } else { uint32_t jpp = (jp - K_p - 1) / 2; kidx = (RM_PERM_TC[jpp / nrows] + NCOLS * (jpp % nrows) + 1) % K_p; if ((kidx - ndummy) < 0) { isdummy = true; } else { isdummy = false; } } if (!isdummy) { temp_table1[k] = jp%(3*nrows*NCOLS); k++; } j++; } for (int i = 0; i < cb_len / 3; i++) { d_i = (i + ndummy) / NCOLS; d_j = (i + ndummy) % NCOLS; for (j = 0; j < 3; j++) { if (j != 2) { kidx = K_p * j + (j + 1) * (RM_PERM_TC[d_j] * nrows + d_i); } else { k = (i + ndummy - 1) % K_p; if (k < 0) k += K_p; kidx = (k / NCOLS + nrows * RM_PERM_TC[k % NCOLS]) % K_p; kidx = 2 * kidx + K_p + 1; } temp_table2[kidx] = 3*i+j; } } for (int i=0;i= in_len) { cp_len = in_len - r_ptr; } srslte_bit_copy(output, w_len+w_offset, w_buff, r_ptr, cp_len); r_ptr += cp_len; if (r_ptr >= in_len) { r_ptr -= in_len; } w_len += cp_len; } return 0; } else { return SRSLTE_ERROR_INVALID_INPUTS; } } int srslte_rm_turbo_rx_lut(int16_t *input, int16_t *output, uint32_t in_len, uint32_t cb_idx, uint32_t rv_idx) { #ifdef LV_HAVE_AVX return srslte_rm_turbo_rx_lut_avx(input, output, in_len, cb_idx, rv_idx); #else #ifdef LV_HAVE_SSE return srslte_rm_turbo_rx_lut_sse(input, output, in_len, cb_idx, rv_idx); #else if (rv_idx < 4 && cb_idx < SRSLTE_NOF_TC_CB_SIZES) { uint32_t out_len = 3*srslte_cbsegm_cbsize(cb_idx)+12; uint16_t *deinter = deinterleaver[cb_idx][rv_idx]; for (int i=0;i= out_len && inputCnt < in_len - 8) { /* Copy last elements */ for (int j=(nwrapps+1)*out_len-4;j<(nwrapps+1)*out_len;j++) { output[deinter[j%out_len]] += input[j]; inputCnt++; } /* And wrap pointers */ nwrapps++; intCnt = 8; xPtr = (const __m128i*) &input[nwrapps*out_len]; lutPtr = (const __m128i*) deinter; } } for (int i=inputCnt;i= out_len && inputCnt < in_len - 16) { /* Copy last elements */ if ((out_len%16) == 12) { for (int j=(nwrapps+1)*out_len-12;j<(nwrapps+1)*out_len;j++) { output[deinter[j%out_len]] += input[j]; inputCnt++; } } else { for (int j=(nwrapps+1)*out_len-4;j<(nwrapps+1)*out_len;j++) { output[deinter[j%out_len]] += input[j]; inputCnt++; } } /* And wrap pointers */ nwrapps++; intCnt = 16; xPtr = (const __m256i*) &input[nwrapps*out_len]; lutPtr = (const __m256i*) deinter; } } for (int i=inputCnt;i w_buff_len) { fprintf(stderr, "Input too large. Max input length including dummy bits is %d (3x%dx32, in_len %d, Kp=%d)\n", w_buff_len, nrows, in_len, K_p); return -1; } ndummy = K_p - in_len / 3; if (ndummy < 0) { ndummy = 0; } if (rv_idx == 0) { /* Sub-block interleaver (5.1.4.1.1) and bit collection */ k = 0; for (s = 0; s < 2; s++) { for (j = 0; j < NCOLS; j++) { for (i = 0; i < nrows; i++) { if (s == 0) { kidx = k % K_p; } else { kidx = K_p + 2 * (k % K_p); } if (i * NCOLS + RM_PERM_TC[j] < ndummy) { w_buff[kidx] = SRSLTE_TX_NULL; } else { w_buff[kidx] = input[(i * NCOLS + RM_PERM_TC[j] - ndummy) * 3 + s]; } k++; } } } // d_k^(2) goes through special permutation for (k = 0; k < K_p; k++) { kidx = (RM_PERM_TC[k / nrows] + NCOLS * (k % nrows) + 1) % K_p; if ((kidx - ndummy) < 0) { w_buff[K_p + 2 * k + 1] = SRSLTE_TX_NULL; } else { w_buff[K_p + 2 * k + 1] = input[3 * (kidx - ndummy) + 2]; } } } /* Bit selection and transmission 5.1.4.1.2 */ N_cb = 3 * K_p; // TODO: Soft buffer size limitation k0 = nrows * (2 * (uint16_t) ceilf((float) N_cb / (float) (8 * nrows)) * rv_idx + 2); k = 0; j = 0; while (k < out_len) { if (w_buff[(k0 + j) % N_cb] != SRSLTE_TX_NULL) { output[k] = w_buff[(k0 + j) % N_cb]; k++; } j++; } return 0; } /* Undoes Turbo Code Rate Matching. * 3GPP TS 36.212 v10.1.0 section 5.1.4.1 * * Soft-combines the data available in w_buff */ int srslte_rm_turbo_rx(float *w_buff, uint32_t w_buff_len, float *input, uint32_t in_len, float *output, uint32_t out_len, uint32_t rv_idx, uint32_t nof_filler_bits) { int nrows, ndummy, K_p, k0, N_cb, jp, kidx; int i, j, k; int d_i, d_j; bool isdummy; nrows = (uint16_t) (out_len / 3 - 1) / NCOLS + 1; K_p = nrows * NCOLS; if (3 * K_p > w_buff_len) { fprintf(stderr, "Output too large. Max output length including dummy bits is %d (3x%dx32, in_len %d)\n", w_buff_len, nrows, out_len); return -1; } if (out_len < 3) { fprintf(stderr, "Error minimum input length for rate matching is 3\n"); return -1; } ndummy = K_p - out_len / 3; if (ndummy < 0) { ndummy = 0; } /* Undo bit collection. Account for dummy bits */ N_cb = 3 * K_p; // TODO: Soft buffer size limitation k0 = nrows * (2 * (uint16_t) ceilf((float) N_cb / (float) (8 * nrows)) * rv_idx + 2); k = 0; j = 0; while (k < in_len) { jp = (k0 + j) % N_cb; if (jp < K_p || !(jp % 2)) { if (jp >= K_p) { d_i = ((jp - K_p) / 2) / nrows; d_j = ((jp - K_p) / 2) % nrows; } else { d_i = jp / nrows; d_j = jp % nrows; } if (d_j * NCOLS + RM_PERM_TC[d_i] >= ndummy) { isdummy = false; if (d_j * NCOLS + RM_PERM_TC[d_i] - ndummy < nof_filler_bits) { isdummy = true; } } else { isdummy = true; } } else { uint16_t jpp = (jp - K_p - 1) / 2; kidx = (RM_PERM_TC[jpp / nrows] + NCOLS * (jpp % nrows) + 1) % K_p; if ((kidx - ndummy) < 0) { isdummy = true; } else { isdummy = false; } } if (!isdummy) { if (w_buff[jp] == SRSLTE_RX_NULL) { w_buff[jp] = input[k]; } else if (input[k] != SRSLTE_RX_NULL) { w_buff[jp] += input[k]; /* soft combine LLRs */ } k++; } j++; } //printf("wbuff:\n"); //srslte_vec_fprint_f(stdout, w_buff, out_len); /* interleaving and bit selection */ for (i = 0; i < out_len / 3; i++) { d_i = (i + ndummy) / NCOLS; d_j = (i + ndummy) % NCOLS; for (j = 0; j < 3; j++) { if (j != 2) { kidx = K_p * j + (j + 1) * (RM_PERM_TC[d_j] * nrows + d_i); } else { k = (i + ndummy - 1) % K_p; if (k < 0) k += K_p; kidx = (k / NCOLS + nrows * RM_PERM_TC[k % NCOLS]) % K_p; kidx = 2 * kidx + K_p + 1; } if (w_buff[kidx] != SRSLTE_RX_NULL) { output[i * 3 + j] = w_buff[kidx]; } else { output[i * 3 + j] = 0; } } } return 0; }