Compare commits

...

7 Commits

Author SHA1 Message Date
Romain Beurdouche
94f30e41cb feat(oai_dfts_armral): PRACH iDFT using ArmRAL
Use ArmRAL FFT for performing iDFT on PRACH

For the tested case (100MHz 4x4 with benetel550, initialDLBWPsearchSpaceZero = 0)
it saves up to 100µs over 950µs in PRACH RX

Warning: 256 bits and 1024 bits iDFT only
For other cases, falls back to the default `libdfts`

One may remove `#define DEFAULT_NEON` to use Armral FFT for every case but this is not working as of this commit
2025-05-22 11:40:55 +00:00
Romain Beurdouche
2da371b02f feat(reverse_bits_u8): Add 16 byte Neon case
Use Neon `vrbitq_u8` to reverse bits in byte on Arm.
2025-05-21 07:02:54 +00:00
Romain Beurdouche
906fcb2091 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.
2025-05-21 07:02:54 +00:00
Romain Beurdouche
ece34ec190 feat(CI): Build Armral LDPC coding in CI
Modified the 7.2 FHI build on native Arm to build Armral LDPC coding.
2025-05-21 07:02:54 +00:00
Romain Beurdouche
baa2903e0d 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.
2025-05-21 07:02:47 +00:00
Romain Beurdouche
e9b75d6b06 feat(nrLDPC_coding): LDPC encoding using ArmRAL
Use `armral_ldpc_rate_matching` for rate matching and `armral_ldpc_encode_block` for encoding.
2025-05-21 06:28:44 +00:00
Romain Beurdouche
aa8eee6531 feat(nrLDPC_coding): LDPC decoding using ArmRAL
Use `armral_ldpc_rate_recovery` for RX rate matching and `armral_ldpc_decode_block` for decoding.
2025-05-21 06:28:44 +00:00
13 changed files with 958 additions and 7 deletions

View File

@@ -61,6 +61,12 @@ endif()
###################################
include(FindPkgConfig)
# Modules that are required
# We provide a find script in cmake_targets/tools/MODULES
# for cmake to find the headers and libraries
###################################
set(CMAKE_MODULE_PATH "${OPENAIR_DIR}/cmake_targets/tools/MODULES" "${CMAKE_MODULE_PATH}")
# Check if asn1c is installed, and check if it supports all options we need
# a user can provide ASN1C_EXEC to override an asn1c to use (e.g., parallel
# installation)
@@ -791,6 +797,24 @@ set(PHY_NRLDPC_CODINGIF
add_library(dfts MODULE ${OPENAIR1_DIR}/PHY/TOOLS/oai_dfts.c ${OPENAIR1_DIR}/PHY/TOOLS/oai_dfts_neon.c)
##########################################################
# DFT library - ArmRAN Acceleration Library
##########################################################
# TODO: Merge ldpc_armral and dfts_armral in one option?
add_boolean_option(ENABLE_DFTS_ARMRAL OFF "Build support for DFT and iDFT with the Arm RAN Acceleration Library" OFF)
if (ENABLE_DFTS_ARMRAL)
find_package(armral REQUIRED)
add_library(dfts_armral MODULE
${OPENAIR1_DIR}/PHY/TOOLS/oai_dfts_armral.c
)
set_target_properties(dfts_armral PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
target_link_libraries(dfts_armral PRIVATE armral)
endif()
add_library(crc_byte OBJECT ${OPENAIR1_DIR}/PHY/CODING/crc_byte.c)
set(PHY_SRC_COMMON
@@ -1694,7 +1718,6 @@ include_directories("${NFAPI_DIR}/pnf_sim/inc")
add_library(oai_iqplayer MODULE
${OPENAIR_DIR}/radio/iqplayer/iqplayer_lib.c
)
set(CMAKE_MODULE_PATH "${OPENAIR_DIR}/cmake_targets/tools/MODULES" "${CMAKE_MODULE_PATH}")
#################################
# add executables for operation

View File

@@ -46,7 +46,7 @@ BUILD_DOXYGEN=0
DISABLE_HARDWARE_DEPENDENCY="False"
CMAKE_BUILD_TYPE="RelWithDebInfo"
CMAKE_CMD="$CMAKE"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_t2 ldpc_xdma websrv oai_iqplayer imscope imscope_record"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_t2 ldpc_xdma ldpc_armral websrv oai_iqplayer imscope imscope_record dfts_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 \
--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_t2)
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;
}

View File

@@ -42,10 +42,14 @@
/* function description array, to be used when loading the dfts/idfts lib */
static loader_shlibfunc_t shlib_fdesc[2];
static loader_shlibfunc_t shlib_fdesc[4];
static char *arg[64] = {"phytest", "-O", "cmdlineonly::dbgl0"};
dftfunc_t dft;
idftfunc_t idft;
dfts_start_t dfts_start;
dfts_stop_t dfts_stop;
int load_dftslib(void)
{
char *ptr = (char *)config_get_if();
@@ -55,9 +59,27 @@ int load_dftslib(void)
}
shlib_fdesc[0].fname = "dft_implementation";
shlib_fdesc[1].fname = "idft_implementation";
shlib_fdesc[2].fname = "dfts_start";
shlib_fdesc[3].fname = "dfts_stop";
int ret = load_module_shlib("dfts", shlib_fdesc, sizeof(shlib_fdesc) / sizeof(loader_shlibfunc_t), NULL);
AssertFatal((ret >= 0), "Error loading dftsc decoder");
AssertFatal((ret >= 0), "Error loading dfts decoder");
dft = (dftfunc_t)shlib_fdesc[0].fptr;
idft = (idftfunc_t)shlib_fdesc[1].fptr;
dfts_start = (dfts_start_t)shlib_fdesc[2].fptr;
dfts_stop = (dfts_stop_t)shlib_fdesc[3].fptr;
dfts_start();
return 0;
}
int free_dftslib()
{
if (dfts_stop) {
dfts_stop();
return 0;
} else {
return -1;
}
}

View File

@@ -7730,6 +7730,12 @@ void idft_implementation(uint8_t sizeidx, int16_t *input, int16_t *output, unsig
idft_ftab[sizeidx].func(input,output,scale_flag);
};
void dfts_start()
{}
void dfts_stop()
{}
#endif
/*---------------------------------------------------------------------------------------*/

View File

@@ -0,0 +1,249 @@
/*
* 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.1 (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
*/
#include <armral.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <pthread.h>
#include <execinfo.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#define debug_msg
#define ONE_OVER_SQRT2_Q15 23170
#define ONE_OVER_SQRT3_Q15 18919
#include "assertions.h"
#include "LOG/log.h"
#include "time_meas.h"
#include "tools_defs.h"
#include "../sse_intrin.h"
#define DEFAULT_NEON
#ifdef DEFAULT_NEON
#include "common/config/config_userapi.h"
#include "common/utils/load_module_shlib.h"
static loader_shlibfunc_t shlib_fdesc[4];
static char *arg[64] = {"phytest", "-O", "cmdlineonly::dbgl0"};
dftfunc_t dft_neon;
idftfunc_t idft_neon;
dfts_start_t dfts_start_neon;
dfts_stop_t dfts_stop_neon;
#endif
#define SZ_PTR(Sz) Sz,
const int dft_stab[] = {FOREACH_DFTSZ(SZ_PTR)};
#define SZ_iPTR(Sz) Sz,
const int idft_stab[] = {FOREACH_IDFTSZ(SZ_iPTR)};
// Pre-allocated plans for PRACH
armral_fft_plan_t *idft_256_cs16_plan_p;
armral_fft_plan_t *idft_1024_cs16_plan_p;
void armral_dft(uint8_t sizeidx, int16_t *x, int16_t *y, unsigned char scale_flag)
{
#ifdef DEFAULT_NEON
dft_neon(sizeidx, x, y, scale_flag);
#else
armral_status status;
switch (sizeidx) {
default:
armral_fft_plan_t *dft_cs16_plan_p;
status = armral_fft_create_plan_cs16(&dft_cs16_plan_p, idft_stab[sizeidx], ARMRAL_FFT_FORWARDS);
AssertFatal(status == ARMRAL_SUCCESS, "Failure in ArmRAL FFT create plan\n");
status = armral_fft_execute_cs16(dft_cs16_plan_p, (armral_cmplx_int16_t *)x, (armral_cmplx_int16_t *)y);
AssertFatal(status == ARMRAL_SUCCESS, "Failure in ArmRAL FFT\n");
status = armral_fft_destroy_plan_cs16(&dft_cs16_plan_p);
AssertFatal(status == ARMRAL_SUCCESS, "Failure in ArmRAL FFT destroy plan\n");
if (scale_flag > 0) {
simd_q15_t *y128 = (simd_q15_t *)y;
int sz = dft_stab[sizeidx];
for (int i = 0; i < (sz / 4); i++) {
y128[i] = shiftright_int16(y128[i], 1);
}
}
break;
}
#endif
}
void armral_idft(uint8_t sizeidx, int16_t *x, int16_t *y, unsigned char scale_flag)
{
armral_status status;
switch (sizeidx) {
case IDFT_256:
status = armral_fft_execute_cs16(idft_256_cs16_plan_p, (armral_cmplx_int16_t *)x, (armral_cmplx_int16_t *)y);
AssertFatal(status == ARMRAL_SUCCESS, "Failure in ArmRAL FFT\n");
if (scale_flag > 0) {
simd_q15_t *y128 = (simd_q15_t *)y;
for (int i = 0; i < 4; i++) {
y128[0] = shiftright_int16(y128[0], 1);
y128[1] = shiftright_int16(y128[1], 1);
y128[2] = shiftright_int16(y128[2], 1);
y128[3] = shiftright_int16(y128[3], 1);
y128[4] = shiftright_int16(y128[4], 1);
y128[5] = shiftright_int16(y128[5], 1);
y128[6] = shiftright_int16(y128[6], 1);
y128[7] = shiftright_int16(y128[7], 1);
y128[8] = shiftright_int16(y128[8], 1);
y128[9] = shiftright_int16(y128[9], 1);
y128[10] = shiftright_int16(y128[10], 1);
y128[11] = shiftright_int16(y128[11], 1);
y128[12] = shiftright_int16(y128[12], 1);
y128[13] = shiftright_int16(y128[13], 1);
y128[14] = shiftright_int16(y128[14], 1);
y128[15] = shiftright_int16(y128[15], 1);
y128 += 16;
}
}
break;
case IDFT_1024:
status = armral_fft_execute_cs16(idft_1024_cs16_plan_p, (armral_cmplx_int16_t *)x, (armral_cmplx_int16_t *)y);
AssertFatal(status == ARMRAL_SUCCESS, "Failure in ArmRAL FFT\n");
if (scale_flag > 0) {
simd_q15_t *y128 = (simd_q15_t *)y;
for (int i = 0; i < 16; i++) {
y128[0] = shiftright_int16(y128[0], 1);
y128[1] = shiftright_int16(y128[1], 1);
y128[2] = shiftright_int16(y128[2], 1);
y128[3] = shiftright_int16(y128[3], 1);
y128[4] = shiftright_int16(y128[4], 1);
y128[5] = shiftright_int16(y128[5], 1);
y128[6] = shiftright_int16(y128[6], 1);
y128[7] = shiftright_int16(y128[7], 1);
y128[8] = shiftright_int16(y128[8], 1);
y128[9] = shiftright_int16(y128[9], 1);
y128[10] = shiftright_int16(y128[10], 1);
y128[11] = shiftright_int16(y128[11], 1);
y128[12] = shiftright_int16(y128[12], 1);
y128[13] = shiftright_int16(y128[13], 1);
y128[14] = shiftright_int16(y128[14], 1);
y128[15] = shiftright_int16(y128[15], 1);
y128 += 16;
}
}
break;
default:
#ifdef DEFAULT_NEON
idft_neon(sizeidx, x, y, scale_flag);
#else
armral_fft_plan_t *idft_cs16_plan_p;
status = armral_fft_create_plan_cs16(&idft_cs16_plan_p, idft_stab[sizeidx], ARMRAL_FFT_BACKWARDS);
AssertFatal(status == ARMRAL_SUCCESS, "Failure in ArmRAL FFT create plan\n");
status = armral_fft_execute_cs16(idft_cs16_plan_p, (armral_cmplx_int16_t *)x, (armral_cmplx_int16_t *)y);
AssertFatal(status == ARMRAL_SUCCESS, "Failure in ArmRAL FFT\n");
status = armral_fft_destroy_plan_cs16(&idft_cs16_plan_p);
AssertFatal(status == ARMRAL_SUCCESS, "Failure in ArmRAL FFT destroy plan\n");
if (scale_flag > 0) {
simd_q15_t *y128 = (simd_q15_t *)y;
int sz = idft_stab[sizeidx];
for (int i = 0; i < (sz / 4); i++) {
y128[i] = shiftright_int16(y128[i], 1);
}
}
#endif
break;
}
}
void dft_implementation(uint8_t sizeidx, int16_t *input, int16_t *output, unsigned char scale_flag)
{
AssertFatal((sizeidx >= 0 && sizeidx < DFT_SIZE_IDXTABLESIZE), "Invalid dft size index %i\n", sizeidx);
int algn = 0xF;
AssertFatal(((intptr_t)output & algn) == 0, "Buffers should be aligned %p", output);
if (((intptr_t)input) & algn) {
int sz = dft_stab[sizeidx];
LOG_D(PHY, "DFT called with input not aligned, add a memcpy, size %d\n", sz);
if (sizeidx == DFT_12) // This case does 8 DFTs in //
sz *= 8;
int16_t tmp[sz * 2] __attribute__((aligned(32))); // input and output are not in right type (int16_t instead of c16_t)
memcpy(tmp, input, sizeof(tmp));
armral_dft(sizeidx, tmp, output, scale_flag);
} else {
armral_dft(sizeidx, input, output, scale_flag);
}
}
void idft_implementation(uint8_t sizeidx, int16_t *input, int16_t *output, unsigned char scale_flag)
{
AssertFatal((sizeidx >= 0 && sizeidx < DFT_SIZE_IDXTABLESIZE), "Invalid idft size index %i\n", sizeidx);
int algn = 0xF;
AssertFatal(((intptr_t)output & algn) == 0, "Buffers should be 16 bytes aligned %p", output);
if (((intptr_t)input) & algn) {
int sz = idft_stab[sizeidx];
LOG_D(PHY, "IDFT called with input not aligned, add a memcpy, size %d\n", sz);
int16_t tmp[sz * 2] __attribute__((aligned(32))); // input and output are not in right type (int16_t instead of c16_t)
memcpy(tmp, input, sizeof(tmp));
armral_idft(sizeidx, tmp, output, scale_flag);
} else {
armral_idft(sizeidx, input, output, scale_flag);
}
}
void dfts_start()
{
armral_status status;
status = armral_fft_create_plan_cs16(&idft_256_cs16_plan_p, 256, ARMRAL_FFT_BACKWARDS);
AssertFatal(status == ARMRAL_SUCCESS, "Failure in ArmRAL FFT create plan\n");
status = armral_fft_create_plan_cs16(&idft_1024_cs16_plan_p, 1024, ARMRAL_FFT_BACKWARDS);
AssertFatal(status == ARMRAL_SUCCESS, "Failure in ArmRAL FFT create plan\n");
#ifdef DEFAULT_NEON
char *ptr = (char *)config_get_if();
if (ptr == NULL) { // phy simulators, config module possibly not loaded
uniqCfg = load_configmodule(3, (char **)arg, CONFIG_ENABLECMDLINEONLY);
logInit();
}
shlib_fdesc[0].fname = "dft_implementation";
shlib_fdesc[1].fname = "idft_implementation";
shlib_fdesc[2].fname = "dfts_start";
shlib_fdesc[3].fname = "dfts_stop";
int ret = load_module_version_shlib("dfts", "", shlib_fdesc, sizeof(shlib_fdesc) / sizeof(loader_shlibfunc_t), NULL);
AssertFatal((ret >= 0), "Error loading dfts decoder");
dft_neon = (dftfunc_t)shlib_fdesc[0].fptr;
idft_neon = (idftfunc_t)shlib_fdesc[1].fptr;
dfts_start_neon = (dfts_start_t)shlib_fdesc[2].fptr;
dfts_stop_neon = (dfts_stop_t)shlib_fdesc[3].fptr;
dfts_start_neon();
#endif
}
void dfts_stop()
{
armral_status status;
status = armral_fft_destroy_plan_cs16(&idft_256_cs16_plan_p);
AssertFatal(status == ARMRAL_SUCCESS, "Failure in ArmRAL FFT destroy plan\n");
status = armral_fft_destroy_plan_cs16(&idft_1024_cs16_plan_p);
AssertFatal(status == ARMRAL_SUCCESS, "Failure in ArmRAL FFT destroy plan\n");
#ifdef DEFAULT_NEON
dfts_stop_neon();
#endif
}

View File

@@ -7126,6 +7126,12 @@ void idft_implementation(uint8_t sizeidx, int16_t *input, int16_t *output, unsig
idft_ftab[sizeidx].func(input,output,scale_flag);
};
void dfts_start()
{}
void dfts_stop()
{}
#endif
/*---------------------------------------------------------------------------------------*/

View File

@@ -630,8 +630,10 @@ void init_fft(uint16_t size,
SZ_DEF(65536) \
SZ_DEF(98304)
typedef void(*dftfunc_t)(uint8_t sizeidx,int16_t *sigF,int16_t *sig,unsigned char scale_flag);
typedef void (*dftfunc_t)(uint8_t sizeidx,int16_t *sigF,int16_t *sig,unsigned char scale_flag);
typedef void (*idftfunc_t)(uint8_t sizeidx, int16_t *sigF, int16_t *sig, unsigned char scale_flag);
typedef void (*dfts_start_t)();
typedef void (*dfts_stop_t)();
extern dftfunc_t dft;
extern idftfunc_t idft;
int load_dftslib(void);