From 27a3e87fb7fec34a6448068dff8ff0fe95d348df Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Thu, 5 Nov 2020 18:55:26 +0100 Subject: [PATCH] Initial DL-SCH encoding --- lib/include/srslte/phy/fec/ldpc/base_graph.h | 1 + lib/include/srslte/phy/phch/sch_nr.h | 113 +++++ lib/src/phy/phch/sch_nr.c | 431 +++++++++++++++++++ 3 files changed, 545 insertions(+) create mode 100644 lib/include/srslte/phy/phch/sch_nr.h create mode 100644 lib/src/phy/phch/sch_nr.c diff --git a/lib/include/srslte/phy/fec/ldpc/base_graph.h b/lib/include/srslte/phy/fec/ldpc/base_graph.h index c7c2ab345..78ae18452 100644 --- a/lib/include/srslte/phy/fec/ldpc/base_graph.h +++ b/lib/include/srslte/phy/fec/ldpc/base_graph.h @@ -43,6 +43,7 @@ #define SRSLTE_LDPC_BG1_MAX_LEN_CB 8448 /*!< \brief Maximum code block size for LDPC BG1 */ #define SRSLTE_LDPC_BG2_MAX_LEN_CB 3840 /*!< \brief Maximum code block size for LDPC BG2 */ +#define SRSLTE_LDPC_MAX_LEN_CB SRSLTE_MAX(SRSLTE_LDPC_BG1_MAX_LEN_CB, SRSLTE_LDPC_BG2_MAX_LEN_CB) #define BG1Nfull 68 /*!< \brief Number of variable nodes in BG1. */ #define BG1N 66 /*!< \brief Number of variable nodes in BG1 after puncturing. */ diff --git a/lib/include/srslte/phy/phch/sch_nr.h b/lib/include/srslte/phy/phch/sch_nr.h new file mode 100644 index 000000000..1c865293e --- /dev/null +++ b/lib/include/srslte/phy/phch/sch_nr.h @@ -0,0 +1,113 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * 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: sch_nr.h + * + * Description: Common UL and DL shared channel encode/decode functions for NR. + * + * Reference: 3GPP TS 38.212 V15.9.0 + *****************************************************************************/ + +#ifndef SRSLTE_SCH_NR_H +#define SRSLTE_SCH_NR_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common_nr.h" +#include "srslte/phy/fec/crc.h" +#include "srslte/phy/fec/ldpc/ldpc_decoder.h" +#include "srslte/phy/fec/ldpc/ldpc_encoder.h" +#include "srslte/phy/fec/ldpc/ldpc_rm.h" +#include "srslte/phy/phch/pdsch_cfg_nr.h" +#include "srslte/phy/phch/ra_nr.h" + +typedef struct SRSLTE_API { + srslte_carrier_nr_t carrier; + + /// Temporal data buffers + uint8_t* cb_in; + + /// CRC generators + srslte_crc_t crc_tb_24; + srslte_crc_t crc_tb_16; + srslte_crc_t crc_cb; + + /// LDPC encoders + srslte_ldpc_encoder_t* encoder_bg1[MAX_LIFTSIZE]; + srslte_ldpc_encoder_t* encoder_bg2[MAX_LIFTSIZE]; + + /// LDPC Rate matcher + srslte_ldpc_rm_t rm; +} srslte_sch_nr_encoder_t; + +typedef struct SRSLTE_API { + bool disable_simd; + bool use_flooded; + float scaling_factor; +} srslte_sch_nr_decoder_cfg_t; + +typedef struct SRSLTE_API { + /// CRC generators + srslte_crc_t crc_tb_24; + srslte_crc_t crc_tb_16; + srslte_crc_t crc_cb; + + /// LDPC decoders + srslte_ldpc_decoder_t* decoder_bg1[MAX_LIFTSIZE]; + srslte_ldpc_decoder_t* decoder_bg2[MAX_LIFTSIZE]; + + /// LDPC Tx/Rx Rate matcher + srslte_ldpc_rm_t rm; +} srslte_sch_nr_decoder_t; + +/** + * @brief Initialises shared channel encoder + * @param q + * @return + */ +SRSLTE_API int srslte_sch_nr_encoder_init(srslte_sch_nr_encoder_t* q); + +SRSLTE_API int srslte_sch_nr_encoder_set_carrier(srslte_sch_nr_encoder_t* q, const srslte_carrier_nr_t* carrier); + +SRSLTE_API int srslte_dlsch_nr_encode(srslte_sch_nr_encoder_t* q, + const srslte_pdsch_cfg_nr_t* cfg, + const srslte_ra_tb_nr_t* tb, + const uint8_t* data, + uint8_t* e_bits); + +SRSLTE_API void srslte_sch_nr_encoder_free(srslte_sch_nr_encoder_t* q); + +/** + * @brief Initialises shared channel decoder + * @param q + * @return + */ +SRSLTE_API int srslte_sch_nr_init_decoder(srslte_sch_nr_decoder_t* q, const srslte_sch_nr_decoder_cfg_t* cfg); + +SRSLTE_API int srslte_dlsch_nr_decode(srslte_sch_nr_decoder_t* q, + const srslte_pdsch_cfg_nr_t* cfg, + const srslte_pdsch_grant_nr_t* grant, + int8_t* e_bits, + uint8_t* data); + +SRSLTE_API void srslte_sch_nr_decoder_free(srslte_sch_nr_decoder_t* q); + +#endif // SRSLTE_SCH_NR_H \ No newline at end of file diff --git a/lib/src/phy/phch/sch_nr.c b/lib/src/phy/phch/sch_nr.c new file mode 100644 index 000000000..a0f0dd041 --- /dev/null +++ b/lib/src/phy/phch/sch_nr.c @@ -0,0 +1,431 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * 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/phy/phch/sch_nr.h" +#include "srslte/config.h" +#include "srslte/phy/fec/cbsegm.h" +#include "srslte/phy/fec/ldpc/ldpc_common.h" +#include "srslte/phy/fec/ldpc/ldpc_rm.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" +#include +#include +#include + +int srslte_sch_nr_encoder_init(srslte_sch_nr_encoder_t* q) +{ + if (q == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (srslte_crc_init(&q->crc_tb_24, SRSLTE_LTE_CRC24A, 24) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + if (srslte_crc_init(&q->crc_cb, SRSLTE_LTE_CRC24B, 24) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + if (srslte_crc_init(&q->crc_tb_16, SRSLTE_LTE_CRC16, 16) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + q->cb_in = srslte_vec_u8_malloc(SRSLTE_LDPC_MAX_LEN_CB); + if (!q->cb_in) { + return SRSLTE_ERROR; + } + +#ifdef LV_HAVE_AVX2 + srslte_ldpc_encoder_type_t encoder_type = SRSLTE_LDPC_ENCODER_AVX2; +#else // LV_HAVE_AVX2 + srslte_ldpc_encoder_type_t encoder_type = SRSLTE_LDPC_ENCODER_C; +#endif // LV_HAVE_AVX2 + + // Iterate over all possible lifting sizes + for (uint16_t ls = 0; ls < MAX_LIFTSIZE; ls++) { + uint8_t ls_index = get_ls_index(ls); + + // Invalid lifting size + if (ls_index == VOID_LIFTSIZE) { + q->encoder_bg1[ls] = NULL; + q->encoder_bg2[ls] = NULL; + continue; + } + + q->encoder_bg1[ls] = calloc(1, sizeof(srslte_ldpc_encoder_t)); + if (!q->encoder_bg1[ls]) { + ERROR("Error: calloc\n"); + return SRSLTE_ERROR; + } + + if (srslte_ldpc_encoder_init(q->encoder_bg1[ls], encoder_type, BG1, ls) < SRSLTE_SUCCESS) { + ERROR("Error: initialising BG1 LDPC encoder for ls=%d\n", ls); + return SRSLTE_ERROR; + } + + q->encoder_bg2[ls] = calloc(1, sizeof(srslte_ldpc_encoder_t)); + if (!q->encoder_bg2[ls]) { + return SRSLTE_ERROR; + } + + if (srslte_ldpc_encoder_init(q->encoder_bg2[ls], encoder_type, BG2, ls) < SRSLTE_SUCCESS) { + ERROR("Error: initialising BG2 LDPC encoder for ls=%d\n", ls); + return SRSLTE_ERROR; + } + } + + return SRSLTE_SUCCESS; +} + +int srslte_sch_nr_encoder_set_carrier(srslte_sch_nr_encoder_t* q, const srslte_carrier_nr_t* carrier) +{ + if (!q) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + q->carrier = *carrier; + + return SRSLTE_SUCCESS; +} + +int srslte_sch_nr_decoder_init(srslte_sch_nr_decoder_t* q, const srslte_sch_nr_decoder_cfg_t* decoder_cfg) +{ + if (q == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (srslte_crc_init(&q->crc_tb_24, SRSLTE_LTE_CRC24A, 24) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + if (srslte_crc_init(&q->crc_cb, SRSLTE_LTE_CRC24B, 24) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + if (srslte_crc_init(&q->crc_tb_16, SRSLTE_LTE_CRC16, 16) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + srslte_ldpc_decoder_type_t decoder_type = SRSLTE_LDPC_DECODER_C; + if (decoder_cfg->use_flooded) { +#ifdef LV_HAVE_AVX2 + if (decoder_cfg->disable_simd) { + decoder_type = SRSLTE_LDPC_DECODER_C_FLOOD; + } else { + decoder_type = SRSLTE_LDPC_DECODER_C_AVX2_FLOOD; + } +#else // LV_HAVE_AVX2 + decoder_type = SRSLTE_LDPC_DECODER_C_FLOOD; +#endif // LV_HAVE_AVX2 + } else { +#ifdef LV_HAVE_AVX2 + if (!decoder_cfg->disable_simd) { + decoder_type = SRSLTE_LDPC_DECODER_C_AVX2; + } +#endif // LV_HAVE_AVX2 + } + + float scaling_factor = isnormal(decoder_cfg->scaling_factor) ? decoder_cfg->scaling_factor : 0.75f; + + // Iterate over all possible lifting sizes + for (uint16_t ls = 0; ls < MAX_LIFTSIZE; ls++) { + uint8_t ls_index = get_ls_index(ls); + + // Invalid lifting size + if (ls_index == VOID_LIFTSIZE) { + q->decoder_bg1[ls] = NULL; + q->decoder_bg2[ls] = NULL; + continue; + } + + q->decoder_bg1[ls] = calloc(1, sizeof(srslte_ldpc_decoder_t)); + if (!q->decoder_bg1[ls]) { + ERROR("Error: calloc\n"); + return SRSLTE_ERROR; + } + + if (srslte_ldpc_decoder_init(q->decoder_bg1[ls], decoder_type, BG1, ls, scaling_factor) < SRSLTE_SUCCESS) { + ERROR("Error: initialising BG1 LDPC decoder for ls=%d\n", ls); + return SRSLTE_ERROR; + } + + q->decoder_bg2[ls] = calloc(1, sizeof(srslte_ldpc_decoder_t)); + if (!q->decoder_bg2[ls]) { + ERROR("Error: calloc\n"); + return SRSLTE_ERROR; + } + + if (srslte_ldpc_decoder_init(q->decoder_bg2[ls], decoder_type, BG2, ls, scaling_factor) < SRSLTE_SUCCESS) { + ERROR("Error: initialising BG2 LDPC decoder for ls=%d\n", ls); + return SRSLTE_ERROR; + } + } + + return SRSLTE_SUCCESS; +} + +int srslte_(srslte_sch_nr_encoder_t* q) +{ + if (q == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (srslte_crc_init(&q->crc_tb_24, SRSLTE_LTE_CRC24A, 24) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + if (srslte_crc_init(&q->crc_cb, SRSLTE_LTE_CRC24B, 24) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + if (srslte_crc_init(&q->crc_tb_16, SRSLTE_LTE_CRC16, 16) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + q->cb_in = srslte_vec_u8_malloc(SRSLTE_LDPC_MAX_LEN_CB); + if (!q->cb_in) { + return SRSLTE_ERROR; + } + +#ifdef LV_HAVE_AVX2 + srslte_ldpc_encoder_type_t encoder_type = SRSLTE_LDPC_ENCODER_AVX2; +#else // LV_HAVE_AVX2 + srslte_ldpc_encoder_type_t encoder_type = SRSLTE_LDPC_ENCODER_C; +#endif // LV_HAVE_AVX2 + + // Iterate over all possible lifting sizes + for (uint16_t ls = 0; ls < MAX_LIFTSIZE; ls++) { + uint8_t ls_index = get_ls_index(ls); + + // Invalid lifting size + if (ls_index == VOID_LIFTSIZE) { + q->encoder_bg1[ls] = NULL; + q->encoder_bg2[ls] = NULL; + continue; + } + + q->encoder_bg1[ls] = calloc(1, sizeof(srslte_ldpc_encoder_t)); + if (!q->encoder_bg1[ls]) { + ERROR("Error: calloc\n"); + return SRSLTE_ERROR; + } + + if (srslte_ldpc_encoder_init(q->encoder_bg1[ls], encoder_type, BG1, ls) < SRSLTE_SUCCESS) { + ERROR("Error: initialising BG1 LDPC encoder for ls=%d\n", ls); + return SRSLTE_ERROR; + } + + q->encoder_bg2[ls] = calloc(1, sizeof(srslte_ldpc_encoder_t)); + if (!q->encoder_bg2[ls]) { + return SRSLTE_ERROR; + } + + if (srslte_ldpc_encoder_init(q->encoder_bg2[ls], encoder_type, BG2, ls) < SRSLTE_SUCCESS) { + ERROR("Error: initialising BG2 LDPC encoder for ls=%d\n", ls); + return SRSLTE_ERROR; + } + } + + return SRSLTE_SUCCESS; +} + +void srslte_sch_nr_free(srslte_sch_nr_encoder_t* q) +{ + if (q == NULL) { + return; + } + + if (q->cb_in) { + free(q->cb_in); + } +} + +/** + * Implementation of TS 38.212 V15.9.0 Table 5.4.2.1-1: Value of n_prb_lbrm + * @param nof_prb Maximum number of PRBs across all configured DL BWPs and UL BWPs of a carrier for DL-SCH and UL-SCH, + * respectively + * @return It returns n_prb_lbrm + */ +uint32_t sch_nr_n_prb_lbrm(uint32_t nof_prb) +{ + if (nof_prb < 33) { + return 32; + } + if (nof_prb <= 66) { + return 32; + } + if (nof_prb <= 107) { + return 107; + } + if (nof_prb <= 135) { + return 135; + } + if (nof_prb <= 162) { + return 162; + } + if (nof_prb <= 217) { + return 217; + } + + return 273; +} + +#define CEIL(NUM, DEN) (((NUM) + ((DEN)-1)) / (DEN)) +#define MOD(NUM, DEN) ((NUM) % (DEN)) + +int srslte_dlsch_nr_encode(srslte_sch_nr_encoder_t* q, + const srslte_pdsch_cfg_nr_t* cfg, + const srslte_ra_tb_nr_t* tb, + const uint8_t* data, + uint8_t* e_bits) +{ + // Pointer protection + if (!q || !cfg || !tb || !data || !e_bits) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + uint8_t* output_ptr = e_bits; + + // TS 38.212 V15.9.0 section 7.2.2 LDPC base graph selection + // if A ≤ 292 , or if A ≤ 3824 and R ≤ 0.67 , or if R ≤ 0 . 25 , LDPC base graph 2 is used; + // otherwise, LDPC base graph 1 is used, + srslte_basegraph_t bg = BG1; + if ((tb->tbs <= 292) || (tb->tbs <= 292 && tb->R <= 0.67) || (tb->R <= 0.25)) { + bg = BG2; + } + + // Compute code block segmentation + srslte_cbsegm_t cbsegm = {}; + if (bg == BG1) { + if (srslte_cbsegm_ldpc_bg1(&cbsegm, tb->tbs) != SRSLTE_SUCCESS) { + ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d\n", tb->tbs); + return SRSLTE_ERROR; + } + } else { + if (srslte_cbsegm_ldpc_bg2(&cbsegm, tb->tbs) != SRSLTE_SUCCESS) { + ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d\n", tb->tbs); + return SRSLTE_ERROR; + } + } + + // Select encoder + if (cbsegm.Z > MAX_LIFTSIZE) { + ERROR("Error: lifting size Z=%d is out-of-range\n", cbsegm.Z); + return SRSLTE_ERROR; + } + srslte_ldpc_encoder_t* encoder = (bg == BG1) ? q->encoder_bg1[cbsegm.Z] : q->encoder_bg2[cbsegm.Z]; + if (encoder == NULL) { + ERROR("Error: encoder for lifting size Z=%d not found\n", cbsegm.Z); + return SRSLTE_ERROR; + } + + // Soft-buffer number of code-block protection + if (tb->softbuffer.tx->max_cb < cbsegm.K1 || tb->softbuffer.tx->max_cb < cbsegm.C) { + return SRSLTE_ERROR; + } + + // Compute derived RM parameters + uint32_t Qm = srslte_mod_bits_x_symbol(tb->mod); + + // Calculate Nref + uint32_t N_re_lbrm = 156 * sch_nr_n_prb_lbrm(q->carrier.nof_prb); + double R_lbrm = 948.0 / 1024.0; + uint32_t Qm_lbrm = (cfg->mcs_table == srslte_mcs_table_256qam) ? 8 : 6; + uint32_t TBS_LRBM = srslte_ra_nr_tbs(N_re_lbrm, 1.0, R_lbrm, Qm_lbrm, cfg->serving_cell_cfg.max_mimo_layers); + uint32_t Nref = ceil(TBS_LRBM / (cbsegm.C * 2.0 / 3.0)); + uint32_t N_L = tb->N_L; + + // Calculate number of code blocks after applying CBGTI + uint32_t Cp = cbsegm.C; // ... obviously, not implemented + + // Select CRC + srslte_crc_t* crc_tb = &q->crc_tb_16; + if (cbsegm.L_tb == 24) { + crc_tb = &q->crc_tb_24; + } + uint32_t checksum_tb = srslte_crc_checksum_byte(crc_tb, data, tb->tbs); + + // Total number of bits, including CRCs + uint32_t Bp = cbsegm.tbs + cbsegm.L_tb + cbsegm.L_cb * cbsegm.C; + + // Number of bits per code block + uint32_t Kp = Bp / cbsegm.C; + + // For each code block... + uint32_t j = 0; + for (uint32_t r = 0; r < cbsegm.C; r++) { + uint8_t* rm_buffer = tb->softbuffer.tx->buffer_b[r]; + if (!rm_buffer) { + ERROR("Error: soft-buffer provided NULL buffer for cb_idx=%d\n", r); + return SRSLTE_ERROR; + } + + // If data provided, encode and store + if (data) { + // If it is the last segment... + if (r == cbsegm.C - 1) { + // Copy payload without TB CRC + srslte_bit_unpack_vector(data, q->cb_in, (int)(Kp - cbsegm.L_cb - cbsegm.L_tb)); + + // Append TB CRC + uint8_t* ptr = &q->cb_in[Kp - cbsegm.L_cb - cbsegm.L_tb]; + srslte_bit_unpack(checksum_tb, &ptr, cbsegm.L_cb); + } else { + // Copy payload + srslte_bit_unpack_vector(data, q->cb_in, (int)(Kp - cbsegm.L_cb)); + } + + // Attach code block CRC if required + if (cbsegm.L_cb) { + srslte_crc_attach(&q->crc_cb, q->cb_in, Kp); + } + + // Insert filler bits + for (uint32_t i = Kp; i < cbsegm.K1; i++) { + q->cb_in[i] = FILLER_BIT; + } + + // Encode code block + srslte_ldpc_encoder_encode(encoder, q->cb_in, rm_buffer, cbsegm.K1); + } + + // Select rate matching output sequence number of bits + uint32_t E = 0; + if (false) { + // if the r -th coded block is not scheduled for transmission as indicated by CBGTI + // ... currently not implemented + } else { + if (r <= (Cp - MOD(tb->nof_bits / (tb->N_L * Qm), Cp) - 1)) { + E = N_L * Qm * (tb->nof_bits / (tb->N_L * Qm * Cp)); + } else { + E = N_L * Qm * CEIL(tb->nof_bits, tb->N_L * Qm * Cp); + } + j++; + } + + // LDPC Rate matching + srslte_ldpc_rm_tx(&q->rm, rm_buffer, output_ptr, E, bg, cbsegm.Z, tb->rv, tb->mod, Nref); + output_ptr += E; + } + + return SRSLTE_SUCCESS; +}