Compare commits

...

6 Commits

Author SHA1 Message Date
Romain Beurdouche
d3378eac2b feat(reverse_bits_u8): Add 16 byte Neon case
Use Neon `vrbitq_u8` to reverse bits in byte on Arm.
2026-01-20 15:59:51 +01:00
Romain Beurdouche
449bf03893 feat(nrLDPC_coding_armral): Add library with ArmRAL decoder only
As of adding this commit, the ArmRAL LDPC encoder is very ineficient.
OAI segment encoder if far better on Arm systems.
But ArmRAL LDPC decoder on the other end is pretty good and outperforms
OAI decoder in some cases in term of SNR and processing time.

Therefore there is a point in having a LDPC coding library with ArmRAL
LDPC decoder and OAI segment encoder.
This is what this commit adds.
2026-01-20 15:59:51 +01:00
Romain Beurdouche
ed587269f9 feat(CI): Build Armral LDPC coding in CI
Modified the 7.2 FHI build on native Arm to build Armral LDPC coding.
2026-01-20 15:59:49 +01:00
Romain Beurdouche
b5c66955b3 feat(nrLDPC_coding_armral): Update for Armral version 25.04
There has been a change of the LDPC decoder interface in ArmRAL
between past version `e9a0dfc702e3e8096e077f3ae1e404bde05407af`
and new version `armral-25.04`

This MR updates the usage of the decoder function accordingly.
2026-01-20 15:58:40 +01:00
Romain Beurdouche
dc0b220276 feat(nrLDPC_coding): LDPC encoding using ArmRAL
Use `armral_ldpc_rate_matching` for rate matching and `armral_ldpc_encode_block` for encoding.
2026-01-20 15:58:40 +01:00
Romain Beurdouche
8c3f7bb79c feat(nrLDPC_coding): LDPC decoding using ArmRAL
Use `armral_ldpc_rate_recovery` for RX rate matching and `armral_ldpc_decode_block` for decoding.
2026-01-20 15:58:37 +01:00
7 changed files with 647 additions and 3 deletions

View File

@@ -41,7 +41,8 @@ BUILD_DIR=ran_build
DISABLE_HARDWARE_DEPENDENCY="False"
CMAKE_BUILD_TYPE="RelWithDebInfo"
CMAKE_CMD="$CMAKE"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_aal ldpc_xdma websrv oai_iqplayer imscope imscope_record"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_aal ldpc_xdma websrv oai_iqplayer imscope imscope_record ldpc_armral"
TARGET_LIST=""
BUILD_TOOL_OPT="-j$(nproc)"

View File

@@ -143,6 +143,20 @@ void reverse_bits_u8(uint8_t const* in, size_t sz, uint8_t* out)
_mm512_storeu_epi8(&out[i], reversed);
}
for (; i < sz; ++i) {
out[i] = bit_reverse_table_256[in[i]];
}
#elif defined(__aarch64__)
int simde_sz = 16;
int i = 0;
int simde_bound = sz - simde_sz;
if((((uintptr_t)in & 63) == 0) && (((uintptr_t)out & 63)== 0)) {
for (; i <= simde_bound; i += simde_sz) {
uint8x16_t input = vld1q_u8(&in[i]);
*((uint8x16_t *)&in[i]) = vrbitq_u8(input);
}
}
for (; i < sz; ++i) {
out[i] = bit_reverse_table_256[in[i]];
}

View File

@@ -64,7 +64,7 @@ RUN git clone https://gerrit.o-ran-sc.org/r/o-du/phy.git /opt/phy && \
## Build Arm RAN Acceleration Library
RUN git clone https://git.gitlab.arm.com/networking/ral.git /opt/ral && \
cd /opt/ral && \
git checkout armral-25.01 &&\
git checkout armral-25.04 &&\
mkdir build && \
cd build && \
cmake -GNinja -DBUILD_SHARED_LIBS=On /opt/ral/ && \
@@ -79,7 +79,7 @@ RUN /bin/sh oaienv && \
mkdir -p log && \
./build_oai \
--ninja --gNB \
--build-lib "telnetsrv enbscope uescope nrscope" \
--build-lib "telnetsrv enbscope uescope nrscope ldpc_armral" \
-t oran_fhlib_5g --cmake-opt -Dxran_LOCATION=/opt/phy/fhi_lib/lib \
--build-e2 --cmake-opt -DXAPP_MULTILANGUAGE=OFF --cmake-opt -DKPM_VERSION=$KPM_VERSION --cmake-opt -DE2AP_VERSION=$E2AP_VERSION \
$BUILD_OPTION && \

View File

@@ -1,3 +1,4 @@
add_subdirectory(nrLDPC_coding_segment)
add_subdirectory(nrLDPC_coding_xdma)
add_subdirectory(nrLDPC_coding_aal)
add_subdirectory(nrLDPC_coding_armral)

View File

@@ -0,0 +1,51 @@
##########################################################
# LDPC coding library - ArmRAN Acceleration Library
##########################################################
add_boolean_option(ENABLE_LDPC_ARMRAL OFF "Build support for LDPC coding with the Arm RAN Acceleration Library" OFF)
if (ENABLE_LDPC_ARMRAL)
find_package(armral REQUIRED)
add_library(ldpc_armral MODULE
nrLDPC_coding_armral_encoder.c
nrLDPC_coding_armral_decoder.c
)
set_target_properties(ldpc_armral PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
target_link_libraries(ldpc_armral PRIVATE ldpc_gen_HEADERS)
target_link_libraries(ldpc_armral PRIVATE armral)
add_dependencies(ldpctest ldpc_armral)
add_dependencies(nr-softmodem ldpc_armral)
add_dependencies(nr-uesoftmodem ldpc_armral)
add_dependencies(nr_ulsim ldpc_armral)
add_dependencies(nr_ulschsim ldpc_armral)
add_dependencies(nr_dlsim ldpc_armral)
add_dependencies(nr_dlschsim ldpc_armral)
add_library(ldpc_armral_dec MODULE
../nrLDPC_coding_segment/nr_rate_matching.c
nrLDPC_coding_armral_decoder.c
../nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c
../../nrLDPC_encoder/ldpc_encoder_optim8segmulti.c
)
set_target_properties(ldpc_armral_dec PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
target_link_libraries(ldpc_armral_dec PRIVATE ldpc_gen_HEADERS)
target_link_libraries(ldpc_armral_dec PRIVATE armral)
add_dependencies(ldpctest ldpc_armral_dec)
add_dependencies(nr-softmodem ldpc_armral_dec)
add_dependencies(nr-uesoftmodem ldpc_armral_dec)
add_dependencies(nr_ulsim ldpc_armral_dec)
add_dependencies(nr_ulschsim ldpc_armral_dec)
add_dependencies(nr_dlsim ldpc_armral_dec)
add_dependencies(nr_dlschsim ldpc_armral_dec)
#ensure that the T header files are generated before targets depending on them
if (${T_TRACER})
add_dependencies(ldpc_armral generate_T)
add_dependencies(ldpc_armral_dec generate_T)
endif (${T_TRACER})
endif()

View File

@@ -0,0 +1,318 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.0 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file PHY/CODING/nrLDPC_coding/nrLDPC_coding_armral/nrLDPC_coding_armral_decoder.c
* \brief Top-level routines for decoding LDPC transport channels using the Arm RAN Acceleration Library
* \author Romain Beurdouche
* \date 2025
* \company EURECOM
* \email romain.beurdouche@eurecom.fr
* \note ArmRAL available at https://git.gitlab.arm.com/networking/ral.git
* \warning
*/
// [from gNB coding]
#include "PHY/defs_gNB.h"
#include "PHY/CODING/coding_extern.h"
#include "PHY/CODING/coding_defs.h"
#include "PHY/CODING/lte_interleaver_inline.h"
#include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_interface.h"
#include "PHY/CODING/nrLDPC_extern.h"
#include "PHY/NR_TRANSPORT/nr_transport_common_proto.h"
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
#include "PHY/NR_TRANSPORT/nr_ulsch.h"
#include "PHY/NR_TRANSPORT/nr_dlsch.h"
#include "SCHED_NR/sched_nr.h"
#include "SCHED_NR/fapi_nr_l1.h"
#include "defs.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
#include "common/utils/LOG/log.h"
#include <stdalign.h>
#include <stdint.h>
#include <syscall.h>
#include <time.h>
#include <armral.h>
// #define gNB_DEBUG_TRACE
#define OAI_LDPC_DECODER_MAX_NUM_LLR 27000 // 26112 // NR_LDPC_NCOL_BG1*NR_LDPC_ZMAX = 68*384
// #define DEBUG_CRC
#ifdef DEBUG_CRC
#define PRINT_CRC_CHECK(a) a
#else
#define PRINT_CRC_CHECK(a)
#endif
#include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_interface.h"
#include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface.h"
/**
* \typedef nrLDPC_decoding_parameters_t
* \struct nrLDPC_decoding_parameters_s
* \brief decoding parameter of transport blocks
* \var A Transport block size (This is A from 38.212 V15.4.0 section 5.1)
* \var K Code block size at decoder output
* \var Z lifting size
* \var F filler bits size
* \var C number of segments
* \var E input llr segment size
* \var BG Base graph index (BG1: 1, BG2: 2)
* \var max_number_iterations maximum number of LDPC iterations
* \var tbslbrm transport block size LBRM in bytes
* \var Qm modulation order
* \var rv_index
* \var llr input llr segment array
* \var d Pointers to code blocks before LDPC decoding (38.212 V15.4.0 section 5.3.2)
* \var d_to_be_cleared
* pointer to the flag used to clear d properly
* when true, clear d after rate dematching
* \var c Pointers to code blocks after LDPC decoding (38.212 V15.4.0 section 5.2.2)
* \var decodeSuccess pointer to the flag indicating that the decoding of the segment was successful
* \var ans pointer to task answer used by the thread pool to detect task completion
* \var abort_decode pointer to decode abort flag
* \var p_ts_rate_unmatch pointer to rate unmatching time stats
* \var p_ts_ldpc_decode pointer to decoding time stats
*/
typedef struct nrLDPC_decoding_parameters_s {
uint32_t A;
uint32_t K;
uint32_t Z;
uint32_t F;
uint32_t C;
uint32_t E;
uint8_t BG;
uint32_t max_number_iterations;
uint32_t tbslbrm;
uint32_t Qm;
uint8_t rv_index;
short *llr;
int16_t *d;
bool *d_to_be_cleared;
uint8_t *c;
bool *decodeSuccess;
task_ans_t *ans;
// decode_abort_t *abort_decode;
time_stats_t *p_ts_rate_unmatch;
time_stats_t *p_ts_ldpc_decode;
} nrLDPC_decoding_parameters_t;
static void nr_process_decode_segment(void *arg)
{
nrLDPC_decoding_parameters_t *rdata = (nrLDPC_decoding_parameters_t *)arg;
const uint32_t A = rdata->A;
const uint32_t K = rdata->K;
const uint32_t Z = rdata->Z;
const uint32_t F = rdata->F;
const uint32_t C = rdata->C;
const uint32_t Kprime = K - rdata->F;
const uint32_t E = rdata->E;
const uint32_t max_number_iterations = rdata->max_number_iterations;
const uint32_t Qm = rdata->Qm;
const uint32_t rv_index = rdata->rv_index;
short *ulsch_llr = rdata->llr;
uint8_t llrProcBuf[OAI_LDPC_DECODER_MAX_NUM_LLR] __attribute__((aligned(32)));
start_meas(rdata->p_ts_rate_unmatch);
armral_ldpc_graph_t armral_bg = rdata->BG == 2 ? LDPC_BASE_GRAPH_2 : LDPC_BASE_GRAPH_1;
uint32_t Nref = 3 * rdata->tbslbrm / (2 * C); // R_LBRM = 2/3
armral_modulation_type armral_mod = ARMRAL_MOD_QPSK;
switch (Qm) {
case 2:
armral_mod = ARMRAL_MOD_QPSK;
break;
case 4:
armral_mod = ARMRAL_MOD_16QAM;
break;
case 6:
armral_mod = ARMRAL_MOD_64QAM;
break;
case 8:
armral_mod = ARMRAL_MOD_256QAM;
break;
default:
LOG_E(PHY, "Modulation order not supported: Qm = %d\n", Qm);
break;
}
int size_f = ceil_mod(E, 16);
int8_t f[size_f] __attribute__((aligned(64)));
memset(f, 0, size_f * sizeof(int8_t));
for (int i = 0; i < (size_f >> 4); i++) {
((simde__m128i *)f)[i] = simde_mm_packs_epi16(((simde__m128i *)ulsch_llr)[2 * i], ((simde__m128i *)ulsch_llr)[2 * i + 1]);
}
if (*rdata->d_to_be_cleared) {
memset(rdata->d, 0, 68 * 384 * sizeof(*rdata->d));
*rdata->d_to_be_cleared = false;
}
armral_status status_rate_recovery =
armral_ldpc_rate_recovery(armral_bg, Z, E, Nref, F, K, rv_index, armral_mod, f, (int8_t *)rdata->d);
if (status_rate_recovery == ARMRAL_ARGUMENT_ERROR) {
LOG_E(PHY, "argument error in armral rate recovery\n");
} else if (status_rate_recovery == ARMRAL_FAIL) {
LOG_E(PHY, "failure in armral rate recovery\n");
}
stop_meas(rdata->p_ts_rate_unmatch);
start_meas(rdata->p_ts_ldpc_decode);
const uint32_t N = rdata->BG == 2 ? 50 * Z : 66 * Z;
int crc_type = crcType(C, A);
uint32_t armral_ldpc_decode_options = ARMRAL_LDPC_DEFAULT_OPTIONS;
armral_ldpc_decode_options |= ARMRAL_LDPC_CRC_EVERY_ITER;
switch (crc_type) {
case CRC24_A:
armral_ldpc_decode_options |= ARMRAL_LDPC_CRC_24A;
break;
case CRC24_B:
armral_ldpc_decode_options |= ARMRAL_LDPC_CRC_24B;
break;
case CRC16:
armral_ldpc_decode_options |= ARMRAL_LDPC_CRC_16;
break;
case CRC8:
armral_ldpc_decode_options |= ARMRAL_LDPC_CRC_NO;
break;
default:
AssertFatal(1, "Invalid crc_type \n");
}
armral_status status_decoding = armral_ldpc_decode_block(N,
(int8_t *)rdata->d,
armral_bg,
Z,
F,
llrProcBuf,
max_number_iterations,
armral_ldpc_decode_options);
if (status_decoding == ARMRAL_ARGUMENT_ERROR) {
LOG_E(PHY, "argument error in armral decoding\n");
}
if (check_crc(llrProcBuf, Kprime, crc_type)) {
memcpy(rdata->c, llrProcBuf, K >> 3);
*rdata->decodeSuccess = true;
} else {
memset(rdata->c, 0, K >> 3);
*rdata->decodeSuccess = false;
}
stop_meas(rdata->p_ts_ldpc_decode);
// Task completed
completed_task_ans(rdata->ans);
}
int nrLDPC_prepare_TB_decoding(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters,
int pusch_id,
thread_info_tm_t *t_info)
{
nrLDPC_TB_decoding_parameters_t *nrLDPC_TB_decoding_parameters = &nrLDPC_slot_decoding_parameters->TBs[pusch_id];
*nrLDPC_TB_decoding_parameters->processedSegments = 0;
for (int r = 0; r < nrLDPC_TB_decoding_parameters->C; r++) {
nrLDPC_decoding_parameters_t *rdata = &((nrLDPC_decoding_parameters_t *)t_info->buf)[t_info->len];
DevAssert(t_info->len < t_info->cap);
rdata->ans = t_info->ans;
t_info->len += 1;
rdata->A = nrLDPC_TB_decoding_parameters->A;
rdata->K = nrLDPC_TB_decoding_parameters->K;
rdata->Z = nrLDPC_TB_decoding_parameters->Z;
rdata->F = nrLDPC_TB_decoding_parameters->F;
rdata->C = nrLDPC_TB_decoding_parameters->C;
rdata->E = nrLDPC_TB_decoding_parameters->segments[r].E;
rdata->BG = nrLDPC_TB_decoding_parameters->BG;
rdata->max_number_iterations = nrLDPC_TB_decoding_parameters->max_ldpc_iterations;
rdata->tbslbrm = nrLDPC_TB_decoding_parameters->tbslbrm;
rdata->Qm = nrLDPC_TB_decoding_parameters->Qm;
rdata->rv_index = nrLDPC_TB_decoding_parameters->rv_index;
rdata->llr = nrLDPC_TB_decoding_parameters->segments[r].llr;
rdata->d = nrLDPC_TB_decoding_parameters->segments[r].d;
rdata->d_to_be_cleared = nrLDPC_TB_decoding_parameters->segments[r].d_to_be_cleared;
rdata->c = nrLDPC_TB_decoding_parameters->segments[r].c;
rdata->decodeSuccess = &nrLDPC_TB_decoding_parameters->segments[r].decodeSuccess;
// rdata->abort_decode = nrLDPC_TB_decoding_parameters->abort_decode;
rdata->p_ts_rate_unmatch = &nrLDPC_TB_decoding_parameters->segments[r].ts_rate_unmatch;
rdata->p_ts_ldpc_decode = &nrLDPC_TB_decoding_parameters->segments[r].ts_ldpc_decode;
task_t t = {.func = &nr_process_decode_segment, .args = rdata};
pushTpool(nrLDPC_slot_decoding_parameters->threadPool, t);
LOG_D(PHY, "Added a block to decode, in pipe: %d\n", r);
}
return nrLDPC_TB_decoding_parameters->C;
}
int32_t nrLDPC_coding_init(void)
{
return 0;
}
int32_t nrLDPC_coding_shutdown(void)
{
return 0;
}
int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_decoding_parameters)
{
int nbSegments = 0;
for (int pusch_id = 0; pusch_id < nrLDPC_slot_decoding_parameters->nb_TBs; pusch_id++) {
nrLDPC_TB_decoding_parameters_t *nrLDPC_TB_decoding_parameters = &nrLDPC_slot_decoding_parameters->TBs[pusch_id];
nbSegments += nrLDPC_TB_decoding_parameters->C;
}
nrLDPC_decoding_parameters_t arr[nbSegments];
task_ans_t ans;
init_task_ans(&ans, nbSegments);
thread_info_tm_t t_info = {.buf = (uint8_t *)arr, .len = 0, .cap = nbSegments, .ans = &ans};
for (int pusch_id = 0; pusch_id < nrLDPC_slot_decoding_parameters->nb_TBs; pusch_id++) {
(void)nrLDPC_prepare_TB_decoding(nrLDPC_slot_decoding_parameters, pusch_id, &t_info);
}
// Execute thread pool tasks
join_task_ans(t_info.ans);
for (int pusch_id = 0; pusch_id < nrLDPC_slot_decoding_parameters->nb_TBs; pusch_id++) {
nrLDPC_TB_decoding_parameters_t *nrLDPC_TB_decoding_parameters = &nrLDPC_slot_decoding_parameters->TBs[pusch_id];
for (int r = 0; r < nrLDPC_TB_decoding_parameters->C; r++) {
if (nrLDPC_TB_decoding_parameters->segments[r].decodeSuccess) {
*nrLDPC_TB_decoding_parameters->processedSegments = *nrLDPC_TB_decoding_parameters->processedSegments + 1;
}
}
}
return 0;
}

View File

@@ -0,0 +1,259 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.0 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c
* \brief Top-level routines for implementing LDPC encoding of transport channels
*/
#include "PHY/defs_gNB.h"
#include "PHY/CODING/coding_extern.h"
#include "PHY/CODING/coding_defs.h"
#include "PHY/CODING/lte_interleaver_inline.h"
#include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_interface.h"
#include "PHY/CODING/nrLDPC_extern.h"
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
#include "PHY/NR_TRANSPORT/nr_transport_common_proto.h"
#include "PHY/NR_TRANSPORT/nr_dlsch.h"
#include "SCHED_NR/sched_nr.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
#include "common/utils/LOG/log.h"
#include "common/utils/nr/nr_common.h"
#include <openair2/UTIL/OPT/opt.h>
#include <syscall.h>
#include "armral.h"
#define DEBUG_LDPC_ENCODING
//#define DEBUG_LDPC_ENCODING_FREE 1
typedef struct ldpc8blocks_args_s {
nrLDPC_TB_encoding_parameters_t *nrLDPC_TB_encoding_parameters;
uint32_t macro_num;
time_stats_t *toutput;
task_ans_t *ans;
} ldpc8blocks_args_t;
static void ldpc8blocks(void *p)
{
ldpc8blocks_args_t *args = (ldpc8blocks_args_t *)p;
nrLDPC_TB_encoding_parameters_t *nrLDPC_TB_encoding_parameters = args->nrLDPC_TB_encoding_parameters;
uint32_t A = nrLDPC_TB_encoding_parameters->A;
uint32_t K = nrLDPC_TB_encoding_parameters->K;
uint32_t Z = nrLDPC_TB_encoding_parameters->Z;
uint32_t F = nrLDPC_TB_encoding_parameters->F;
uint32_t C = nrLDPC_TB_encoding_parameters->C;
uint8_t Qm = nrLDPC_TB_encoding_parameters->Qm;
uint16_t nb_rb = nrLDPC_TB_encoding_parameters->nb_rb;
unsigned int G = nrLDPC_TB_encoding_parameters->G;
LOG_D(PHY, "dlsch coding A %d K %d G %d (nb_rb %d, Qm %d)\n", A, K, G, nb_rb, (int)Qm);
armral_ldpc_graph_t armral_bg = nrLDPC_TB_encoding_parameters->BG == 2 ? LDPC_BASE_GRAPH_2 : LDPC_BASE_GRAPH_1;
uint32_t Nref = 3 * nrLDPC_TB_encoding_parameters->tbslbrm / (2 * C); // R_LBRM = 2/3
armral_modulation_type armral_mod = ARMRAL_MOD_QPSK;
switch (Qm) {
case 2:
armral_mod = ARMRAL_MOD_QPSK;
break;
case 4:
armral_mod = ARMRAL_MOD_16QAM;
break;
case 6:
armral_mod = ARMRAL_MOD_64QAM;
break;
case 8:
armral_mod = ARMRAL_MOD_256QAM;
break;
default:
LOG_E(PHY, "Modulation order not supported: Qm = %d\n", Qm);
break;
}
// 384 (lifting size in bits) / 8 = 48 (lifting size in bytes)
uint8_t d[68 * 48] __attribute__((aligned(64)));
uint8_t f[68 * 48] __attribute__((aligned(64)));
unsigned int macro_segment = 8 * args->macro_num;
unsigned int macro_segment_end = (C > 8 * (args->macro_num + 1)) ? 8 * (args->macro_num + 1) : C;
unsigned int offset_output_bit = 0;
// Calculate initial offset in output buffer
for (int r = 0; r < macro_segment; r++) {
offset_output_bit += nrLDPC_TB_encoding_parameters->segments[r].E;
}
for (int r = macro_segment; r < macro_segment_end; r++) {
start_meas(&nrLDPC_TB_encoding_parameters->segments[r].ts_ldpc_encode);
memset(d, 0, 68 * 48 * sizeof(*d));
armral_status status_encoding = armral_ldpc_encode_block(nrLDPC_TB_encoding_parameters->segments[r].c, armral_bg, Z, F, d);
if (status_encoding == ARMRAL_ARGUMENT_ERROR) {
LOG_E(PHY, "argument error in armral encoding\n");
} else if (status_encoding == ARMRAL_FAIL) {
LOG_E(PHY, "failure in armral encoding\n");
}
stop_meas(&nrLDPC_TB_encoding_parameters->segments[r].ts_ldpc_encode);
start_meas(&nrLDPC_TB_encoding_parameters->segments[r].ts_rate_match);
unsigned int E = nrLDPC_TB_encoding_parameters->segments[r].E;
LOG_D(NR_PHY,
"Rate Matching, Code segment %d/%d (coded bits (G) %u, E %d, Filler bits %d, Filler offset %d Qm %d, nb_rb "
"%d,nrOfLayer %d)...\n",
r,
C,
G,
E,
F,
K - F - 2 * Z,
Qm,
nb_rb,
nrLDPC_TB_encoding_parameters->nb_layers);
if (K - F - 2 * Z > E) {
LOG_E(PHY, "dlsch coding A %d Kr %d G %d (nb_rb %d, Qm %d)\n", A, K, G, nb_rb, Qm);
LOG_E(NR_PHY,
"Rate Matching, Code segments %d/%d (coded bits (G) %u, E %d, Kr %d, Filler bits %d, Filler offset %d Qm %d, "
"nb_rb %d)...\n",
macro_segment,
C,
G,
E,
K,
F,
K - F - 2 * Z,
Qm,
nb_rb);
}
memset(f, 0, 68 * 48 * sizeof(*f));
armral_status status_rate_matching =
armral_ldpc_rate_matching(armral_bg, Z, E, Nref, F, K, nrLDPC_TB_encoding_parameters->rv_index, armral_mod, d, f);
if (status_rate_matching == ARMRAL_ARGUMENT_ERROR) {
LOG_E(PHY, "argument error in armral rate matching\n");
} else if (status_rate_matching == ARMRAL_FAIL) {
LOG_E(PHY, "failure in armral rate matching\n");
}
stop_meas(&nrLDPC_TB_encoding_parameters->segments[r].ts_rate_match);
if (args->toutput != NULL)
start_meas(args->toutput);
unsigned int f_reverse_size = (E & 7) == 0 ? E >> 3 : (E >> 3) + 1;
reverse_bits_u8(f, f_reverse_size, f);
if ((offset_output_bit & 7) == 0) {
LOG_D(PHY, "encoder output aligned on byte, using memcpy\n");
unsigned int output_copy_size = (E & 7) == 0 ? E >> 3 : (E >> 3) + 1;
memcpy(&nrLDPC_TB_encoding_parameters->output[offset_output_bit >> 3], f, output_copy_size);
} else {
LOG_D(PHY, "encoder output NOT aligned on byte, using Neon\n");
uint64_t *f_64 = (uint64_t *)f;
unsigned int output_offset_64 = (offset_output_bit >> 3) - ((offset_output_bit >> 3) & 7);
uint64_t *output_64 = (uint64_t *)&nrLDPC_TB_encoding_parameters->output[output_offset_64];
unsigned int nb_vec = (E & 63) == 0 ? E >> 6 : (E >> 6) + 1;
int64_t shift_bit_low = offset_output_bit & 63;
int64x1_t shift_bit_low_64x1 = vld1_s64(&shift_bit_low);
int64_t shift_bit_high = shift_bit_low - 64;
int64x1_t shift_bit_high_64x1 = vld1_s64(&shift_bit_high);
for (int i = 0; i < nb_vec; i++) {
uint64x1_t f_64x1 = vld1_u64(&f_64[i]);
uint64x1_t f_64x1_low = vshl_u64(f_64x1, shift_bit_low_64x1);
uint64x1_t output_64x1_low = vld1_u64(&output_64[i]);
output_64[i] = (uint64_t)vorr_u64(output_64x1_low, f_64x1_low);
uint64x1_t f_64x1_high = vshl_u64(f_64x1, shift_bit_high_64x1);
uint64x1_t output_64x1_high = vld1_u64(&output_64[i + 1]);
output_64[i + 1] = (uint64_t)vorr_u64(output_64x1_high, f_64x1_high);
}
}
if (args->toutput != NULL)
stop_meas(args->toutput);
// Increment offset in output buffer
offset_output_bit += E;
// TODO Manage race condition every 8 segment end
}
// Task running in // completed
completed_task_ans(args->ans);
}
static int nrLDPC_prepare_TB_encoding(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters,
int dlsch_id,
thread_info_tm_t *t_info)
{
nrLDPC_TB_encoding_parameters_t *nrLDPC_TB_encoding_parameters = &nrLDPC_slot_encoding_parameters->TBs[dlsch_id];
uint32_t C = nrLDPC_TB_encoding_parameters->C;
size_t const n_seg = (C / 8 + ((C & 7) == 0 ? 0 : 1));
for (int j = 0; j < n_seg; j++) {
ldpc8blocks_args_t *perJobImpp = &((ldpc8blocks_args_t *)t_info->buf)[t_info->len];
DevAssert(t_info->len < t_info->cap);
perJobImpp->ans = t_info->ans;
t_info->len += 1;
perJobImpp->macro_num = j;
perJobImpp->nrLDPC_TB_encoding_parameters = nrLDPC_TB_encoding_parameters;
perJobImpp->toutput = nrLDPC_slot_encoding_parameters->toutput;
task_t t = {.func = ldpc8blocks, .args = perJobImpp};
pushTpool(nrLDPC_slot_encoding_parameters->threadPool, t);
}
return n_seg;
}
int nrLDPC_coding_encoder(nrLDPC_slot_encoding_parameters_t *nrLDPC_slot_encoding_parameters)
{
int nbTasks = 0;
for (int dlsch_id = 0; dlsch_id < nrLDPC_slot_encoding_parameters->nb_TBs; dlsch_id++) {
nrLDPC_TB_encoding_parameters_t *nrLDPC_TB_encoding_parameters = &nrLDPC_slot_encoding_parameters->TBs[dlsch_id];
uint32_t C = nrLDPC_TB_encoding_parameters->C;
size_t n_seg = (C / 8 + ((C & 7) == 0 ? 0 : 1));
nbTasks += n_seg;
}
ldpc8blocks_args_t arr[nbTasks];
task_ans_t ans;
init_task_ans(&ans, nbTasks);
thread_info_tm_t t_info = {.buf = (uint8_t *)arr, .len = 0, .cap = nbTasks, .ans = &ans};
int nbEncode = 0;
for (int dlsch_id = 0; dlsch_id < nrLDPC_slot_encoding_parameters->nb_TBs; dlsch_id++) {
nbEncode += nrLDPC_prepare_TB_encoding(nrLDPC_slot_encoding_parameters, dlsch_id, &t_info);
}
if (nbEncode < nbTasks) {
completed_many_task_ans(&ans, nbTasks - nbEncode);
}
// Execute thread pool tasks
join_task_ans(&ans);
return 0;
}