mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-21 00:20:30 +00:00
Compare commits
13 Commits
ldpc_decod
...
uci_on_pus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3cfbff22f | ||
|
|
03f55a1616 | ||
|
|
a47a3d7aea | ||
|
|
215f881ebe | ||
|
|
8215e06c3d | ||
|
|
9fa8564858 | ||
|
|
8145f91ad7 | ||
|
|
3b4bbe8891 | ||
|
|
904b8df586 | ||
|
|
1dd43cd6db | ||
|
|
a98a6eae30 | ||
|
|
f496294186 | ||
|
|
db092ffd6c |
@@ -94,6 +94,12 @@ RUN ldconfig && \
|
||||
/usr/local/lib/libdfts.so \
|
||||
/usr/local/lib/libldpc*.so
|
||||
|
||||
# Copy test vectors used by phySim
|
||||
WORKDIR /opt/oai-physim/
|
||||
COPY --from=phy-sim-build \
|
||||
/oai-ran/cmake_targets/ran_build/build/openair1/PHY/CODING/tests/*.bin \
|
||||
/opt/oai-physim/
|
||||
|
||||
# Copy the relevant configuration files for phySim
|
||||
WORKDIR /opt/oai-physim/
|
||||
|
||||
|
||||
@@ -31,11 +31,14 @@
|
||||
*/
|
||||
|
||||
#include "PHY/CODING/nrSmallBlock/nr_small_block_defs.h"
|
||||
#include "common/utils/assertions.h"
|
||||
|
||||
//input = [0 ... 0 c_K-1 ... c_2 c_1 c_0]
|
||||
//output = [d_31 d_30 ... d_2 d_1 d_0]
|
||||
uint32_t encodeSmallBlock(int in, int len)
|
||||
{
|
||||
AssertFatal(len >= 3, "encodeSmallBlock only supports input lengths A >= 3, got A=%d", len);
|
||||
|
||||
uint32_t out = 0;
|
||||
for (int i = 0; i < len; i++)
|
||||
if ((in & (1 << i)) > 0)
|
||||
|
||||
@@ -205,6 +205,12 @@ add_physim_test(5g nr_ulsim misc.test12 "32 PRBs, 120 kHz SCS" -n300 -s5 -r32 -R
|
||||
add_physim_test(5g nr_ulsim misc.test13 "MCS 0, low SNR performance" -n300 -m0 -S -0.6 -i 1,0)
|
||||
add_physim_test(5g nr_ulsim misc.test14 "MCS 28, 106 PRBs, Time shift 8" -n300 -m28 -R106 -r106 -t90 -s24 -S24 -d 8)
|
||||
add_physim_test(5g nr_ulsim misc.test15 "SRS, SNR 40 dB" -n300 -s40 -E 1)
|
||||
add_physim_test(5g nr_ulsim misc.test16 "UCI on PUSCH: 1 OACK" -m 27 -u 1 -R 51 -r 51 -o uci_on_pusch_1.bin)
|
||||
configure_file("${CMAKE_SOURCE_DIR}/openair1/SIMULATION/TOOLS/uci_on_pusch_1.bin" "${CMAKE_CURRENT_BINARY_DIR}/uci_on_pusch_1.bin" COPYONLY)
|
||||
add_physim_test(5g nr_ulsim misc.test17 "UCI on PUSCH: 2 OACK" -m 27 -u 1 -R 51 -r 51 -o uci_on_pusch_2.bin)
|
||||
configure_file("${CMAKE_SOURCE_DIR}/openair1/SIMULATION/TOOLS/uci_on_pusch_2.bin" "${CMAKE_CURRENT_BINARY_DIR}/uci_on_pusch_2.bin" COPYONLY)
|
||||
add_physim_test(5g nr_ulsim misc.test18 "UCI on PUSCH: 3 OACK" -m 27 -u 1 -R 51 -r 51 -o uci_on_pusch_3.bin)
|
||||
configure_file("${CMAKE_SOURCE_DIR}/openair1/SIMULATION/TOOLS/uci_on_pusch_3.bin" "${CMAKE_CURRENT_BINARY_DIR}/uci_on_pusch_3.bin" COPYONLY)
|
||||
add_physim_test(5g nr_ulsim sc-fdma.test1 "SC-FDMA, 50 PRBs" -n300 -s5 -Z)
|
||||
add_physim_test(5g nr_ulsim sc-fdma.test2 "SC-FDMA, 75 PRBs" -n300 -s5 -Z -r75)
|
||||
add_physim_test(5g nr_ulsim sc-fdma.test3 "SC-FDMA, 216 PRBs" -n150 -s5 -Z -r216 -R217)
|
||||
|
||||
@@ -306,6 +306,10 @@ int8_t get_next_dmrs_symbol_in_slot(uint16_t ul_dmrs_symb_pos, uint8_t counter,
|
||||
return -1;
|
||||
}
|
||||
|
||||
int8_t get_num_dmrs_re_per_rb(const uint8_t dmrs_type, const uint8_t num_cdm_grp_no_data)
|
||||
{
|
||||
return (dmrs_type == NFAPI_NR_DMRS_TYPE1 ? 6 * num_cdm_grp_no_data : 4 * num_cdm_grp_no_data);
|
||||
}
|
||||
|
||||
/* return the position of valid dmrs symbol in a slot for channel compensation */
|
||||
int8_t get_valid_dmrs_idx_for_channel_est(uint16_t dmrs_symb_pos, uint8_t counter)
|
||||
|
||||
@@ -71,6 +71,8 @@ void nr_chest_time_domain_avg(NR_DL_FRAME_PARMS *frame_parms,
|
||||
uint16_t dmrs_bitmap,
|
||||
uint16_t num_rbs);
|
||||
|
||||
int8_t get_num_dmrs_re_per_rb(const uint8_t dmrs_type, const uint8_t num_cdm_grp_no_data);
|
||||
|
||||
static inline uint8_t is_dmrs_symbol(uint8_t l, uint16_t dmrsSymbMask)
|
||||
{
|
||||
DevAssert(l < 32);
|
||||
|
||||
@@ -41,6 +41,15 @@
|
||||
#define NR_PUSCH_x 2 // UCI placeholder bit TS 38.212 V15.4.0 subclause 5.3.3.1
|
||||
#define NR_PUSCH_y 3 // UCI placeholder bit
|
||||
|
||||
typedef enum {
|
||||
BIT_TYPE_ULSCH = 0, // Default: UL-SCH data
|
||||
BIT_TYPE_ACK = 1, // HARQ-ACK bit
|
||||
BIT_TYPE_ACK_RESERVED = 2, // Reserved for HARQ-ACK (punctured)
|
||||
BIT_TYPE_ACK_ULSCH = 3,
|
||||
BIT_TYPE_CSI1 = 4, // CSI Part 1 bit
|
||||
BIT_TYPE_CSI2 = 5 // CSI Part 2 bit
|
||||
} uci_on_pusch_bit_type_t;
|
||||
|
||||
// Specifies the data that should be copied to the scope during PDSCH RX
|
||||
typedef struct pdsch_scope_req_s {
|
||||
bool copy_chanest_to_scope;
|
||||
@@ -93,6 +102,13 @@ void nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
|
||||
int nb_dlsch,
|
||||
uint8_t *DLSCH_ids);
|
||||
|
||||
int nr_ulsch_pre_encoding(PHY_VARS_NR_UE *ue,
|
||||
const NR_UE_ULSCH_t *ulsch,
|
||||
const uint32_t frame,
|
||||
const uint8_t slot,
|
||||
const unsigned int *G,
|
||||
const int nb_ulsch,
|
||||
const uint8_t *ULSCH_ids);
|
||||
/** \brief This is the alternative top-level entry point for ULSCH encoding in UE.
|
||||
It handles all the HARQ processes in only one call. The routine first
|
||||
computes the segmentation information, followed by LDPC encoding algorithm of the
|
||||
@@ -128,8 +144,8 @@ void nr_pusch_codeword_scrambling(uint8_t *in,
|
||||
uint32_t Nid,
|
||||
uint32_t n_RNTI,
|
||||
bool uci_on_pusch,
|
||||
uint32_t* out);
|
||||
|
||||
const uci_on_pusch_bit_type_t *template,
|
||||
uint32_t *out);
|
||||
|
||||
/** \brief Alternative entry point to UE uplink shared channels procedures.
|
||||
It handles all the HARQ processes in only one call.
|
||||
@@ -161,7 +177,8 @@ uint8_t nr_ue_pusch_common_procedures(PHY_VARS_NR_UE *UE,
|
||||
c16_t **txdataF,
|
||||
c16_t **txdata,
|
||||
uint32_t linktype,
|
||||
bool was_symbol_used[NR_NUMBER_OF_SYMBOLS_PER_SLOT]);
|
||||
bool was_symbol_used[NR_NUMBER_OF_SYMBOLS_PER_SLOT],
|
||||
bool no_phase_pre_comp);
|
||||
|
||||
void clean_UE_harq(PHY_VARS_NR_UE *UE);
|
||||
|
||||
|
||||
@@ -66,6 +66,8 @@ typedef struct {
|
||||
uint32_t C;
|
||||
/// Number of bits in code segments
|
||||
uint32_t K;
|
||||
///
|
||||
uint32_t Kb;
|
||||
/// Total number of bits across all segments
|
||||
uint32_t sumKr;
|
||||
/// Number of "Filler" bits
|
||||
@@ -152,4 +154,13 @@ typedef struct {
|
||||
// PTRS symbol index, to be updated every PTRS symbol within a slot.
|
||||
uint8_t ptrs_symbol_index;
|
||||
} NR_UE_DLSCH_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t Q_dash_ACK; // number of coded HARQ-ACK symbols
|
||||
uint16_t E_uci_ACK; // number of coded HARQ-ACK bits
|
||||
uint16_t Q_dash_ACK_rvd; // number of coded HARQ-ACK symbols reserved
|
||||
uint16_t E_uci_ACK_rvd; // number of coded HARQ-ACK bits reserved
|
||||
uint32_t G_ulsch; // bit capacity of ULSCH
|
||||
} rate_match_info_uci_t;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -34,56 +34,27 @@
|
||||
#include "common/utils/LOG/vcd_signal_dumper.h"
|
||||
#include "PHY/log_tools.h"
|
||||
|
||||
int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
|
||||
NR_UE_ULSCH_t *ulsch,
|
||||
const uint32_t frame,
|
||||
const uint8_t slot,
|
||||
unsigned int *G,
|
||||
int nb_ulsch,
|
||||
uint8_t *ULSCH_ids,
|
||||
uint16_t number_dmrs_symbols)
|
||||
int nr_ulsch_pre_encoding(PHY_VARS_NR_UE *ue,
|
||||
const NR_UE_ULSCH_t *ulsch,
|
||||
const uint32_t frame,
|
||||
const uint8_t slot,
|
||||
const unsigned int *G,
|
||||
const int nb_ulsch,
|
||||
const uint8_t *ULSCH_ids)
|
||||
{
|
||||
start_meas_nr_ue_phy(ue, ULSCH_ENCODING_STATS);
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_UE_ULSCH_ENCODING, VCD_FUNCTION_IN);
|
||||
|
||||
nrLDPC_TB_encoding_parameters_t TBs[nb_ulsch];
|
||||
memset(TBs, 0, sizeof(TBs));
|
||||
nrLDPC_slot_encoding_parameters_t slot_parameters = {
|
||||
.frame = frame,
|
||||
.slot = slot,
|
||||
.nb_TBs = nb_ulsch,
|
||||
.threadPool = &get_nrUE_params()->Tpool,
|
||||
.tinput = NULL,
|
||||
.tprep = NULL,
|
||||
.tparity = NULL,
|
||||
.toutput = NULL,
|
||||
.TBs = TBs
|
||||
};
|
||||
|
||||
int max_num_segments = 0;
|
||||
|
||||
for (uint8_t pusch_id = 0; pusch_id < nb_ulsch; pusch_id++) {
|
||||
uint8_t ULSCH_id = ULSCH_ids[pusch_id];
|
||||
uint8_t harq_pid = ulsch[ULSCH_id].pusch_pdu.pusch_data.harq_process_id;
|
||||
|
||||
nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[pusch_id];
|
||||
/* Neither harq_pid nor ULSCH_id are unique in the instance
|
||||
* but their combination is.
|
||||
* Since ULSCH_id < 2
|
||||
* then 2 * harq_pid + ULSCH_id is unique.
|
||||
*/
|
||||
TB_parameters->harq_unique_pid = 2 * harq_pid + ULSCH_id;
|
||||
for (uint_fast8_t pusch_id = 0; pusch_id < nb_ulsch; pusch_id++) {
|
||||
const uint8_t ULSCH_id = ULSCH_ids[pusch_id];
|
||||
const uint8_t harq_pid = ulsch[ULSCH_id].pusch_pdu.pusch_data.harq_process_id;
|
||||
|
||||
/////////////////////////parameters and variables initialization/////////////////////////
|
||||
|
||||
unsigned int crc = 1;
|
||||
NR_UL_UE_HARQ_t *harq_process = &ue->ul_harq_processes[harq_pid];
|
||||
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &ulsch->pusch_pdu;
|
||||
uint16_t nb_rb = pusch_pdu->rb_size;
|
||||
uint32_t A = pusch_pdu->pusch_data.tb_size << 3;
|
||||
uint8_t Qm = pusch_pdu->qam_mod_order;
|
||||
const uint16_t nb_rb = pusch_pdu->rb_size;
|
||||
const uint32_t A = pusch_pdu->pusch_data.tb_size << 3;
|
||||
const uint8_t Qm = pusch_pdu->qam_mod_order;
|
||||
// target_code_rate is in 0.1 units
|
||||
float Coderate = (float)pusch_pdu->target_code_rate / 10240.0f;
|
||||
const float Coderate = (float)pusch_pdu->target_code_rate / 10240.0f;
|
||||
|
||||
LOG_D(NR_PHY, "ulsch coding nb_rb %d, Nl = %d\n", nb_rb, pusch_pdu->nrOfLayers);
|
||||
LOG_D(NR_PHY, "ulsch coding A %d G %d mod_order %d Coderate %f\n", A, G[pusch_id], Qm, Coderate);
|
||||
@@ -91,11 +62,11 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
|
||||
|
||||
///////////////////////// a---->| add CRC |---->b /////////////////////////
|
||||
|
||||
int max_payload_bytes = MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER * pusch_pdu->nrOfLayers * 1056;
|
||||
const int max_payload_bytes = MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER * pusch_pdu->nrOfLayers * 1056;
|
||||
int B;
|
||||
if (A > NR_MAX_PDSCH_TBS) {
|
||||
// Add 24-bit crc (polynomial A) to payload
|
||||
crc = crc24a(harq_process->payload_AB, A) >> 8;
|
||||
const unsigned int crc = crc24a(harq_process->payload_AB, A) >> 8;
|
||||
harq_process->payload_AB[A >> 3] = ((uint8_t *)&crc)[2];
|
||||
harq_process->payload_AB[1 + (A >> 3)] = ((uint8_t *)&crc)[1];
|
||||
harq_process->payload_AB[2 + (A >> 3)] = ((uint8_t *)&crc)[0];
|
||||
@@ -103,7 +74,7 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
|
||||
AssertFatal((A / 8) + 4 <= max_payload_bytes, "A %d is too big (A/8+4 = %d > %d)\n", A, (A / 8) + 4, max_payload_bytes);
|
||||
} else {
|
||||
// Add 16-bit crc (polynomial A) to payload
|
||||
crc = crc16(harq_process->payload_AB, A) >> 16;
|
||||
const unsigned int crc = crc16(harq_process->payload_AB, A) >> 16;
|
||||
harq_process->payload_AB[A >> 3] = ((uint8_t *)&crc)[1];
|
||||
harq_process->payload_AB[1 + (A >> 3)] = ((uint8_t *)&crc)[0];
|
||||
B = A + 16;
|
||||
@@ -151,7 +122,7 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
|
||||
T_INT((int)ulsch->pusch_pdu.transform_precoding), // transformPrecoder_enabled = 0, transformPrecoder_disabled = 1
|
||||
T_INT((int)ulsch->pusch_pdu.dmrs_config_type), // dmrs_resource_map_config: pusch_dmrs_type1 = 0, pusch_dmrs_type2 = 1
|
||||
T_INT((int)ulsch->pusch_pdu.ul_dmrs_symb_pos), // used to derive the DMRS symbol positions
|
||||
T_INT((int)number_dmrs_symbols),
|
||||
T_INT((int)get_num_dmrs(ulsch->pusch_pdu.ul_dmrs_symb_pos)),
|
||||
// dmrs_start_ofdm_symbol
|
||||
// dmrs_duration_num_ofdm_symbols
|
||||
// dmrs_num_add_positions
|
||||
@@ -168,36 +139,55 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
|
||||
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_SEGMENTATION, VCD_FUNCTION_IN);
|
||||
start_meas_nr_ue_phy(ue, ULSCH_SEGMENTATION_STATS);
|
||||
TB_parameters->Kb = nr_segmentation(harq_process->payload_AB,
|
||||
harq_process->c,
|
||||
B,
|
||||
&harq_process->C,
|
||||
&harq_process->K,
|
||||
&harq_process->Z,
|
||||
&harq_process->F,
|
||||
harq_process->BG);
|
||||
TB_parameters->C = harq_process->C;
|
||||
TB_parameters->K = harq_process->K;
|
||||
TB_parameters->Z = harq_process->Z;
|
||||
TB_parameters->F = harq_process->F;
|
||||
TB_parameters->BG = harq_process->BG;
|
||||
if (TB_parameters->C > MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER * pusch_pdu->nrOfLayers) {
|
||||
LOG_E(PHY, "nr_segmentation.c: too many segments %d, B %d\n", TB_parameters->C, B);
|
||||
harq_process->Kb = nr_segmentation(harq_process->payload_AB,
|
||||
harq_process->c,
|
||||
B,
|
||||
&harq_process->C,
|
||||
&harq_process->K,
|
||||
&harq_process->Z,
|
||||
&harq_process->F,
|
||||
harq_process->BG);
|
||||
if (harq_process->C > MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER * pusch_pdu->nrOfLayers) {
|
||||
LOG_E(PHY, "nr_segmentation.c: too many segments %d, B %d\n", harq_process->C, B);
|
||||
return (-1);
|
||||
}
|
||||
stop_meas_nr_ue_phy(ue, ULSCH_SEGMENTATION_STATS);
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_SEGMENTATION, VCD_FUNCTION_OUT);
|
||||
max_num_segments = max(max_num_segments, TB_parameters->C);
|
||||
|
||||
TB_parameters->nb_rb = nb_rb;
|
||||
TB_parameters->Qm = Qm;
|
||||
TB_parameters->mcs = pusch_pdu->mcs_index;
|
||||
TB_parameters->nb_layers = pusch_pdu->nrOfLayers;
|
||||
TB_parameters->rv_index = pusch_pdu->pusch_data.rv_index;
|
||||
TB_parameters->G = G[pusch_id];
|
||||
TB_parameters->tbslbrm = pusch_pdu->tbslbrm;
|
||||
TB_parameters->A = A;
|
||||
} // pusch_id
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
|
||||
NR_UE_ULSCH_t *ulsch,
|
||||
const uint32_t frame,
|
||||
const uint8_t slot,
|
||||
unsigned int *G,
|
||||
int nb_ulsch,
|
||||
uint8_t *ULSCH_ids,
|
||||
uint16_t number_dmrs_symbols)
|
||||
{
|
||||
start_meas_nr_ue_phy(ue, ULSCH_ENCODING_STATS);
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_UE_ULSCH_ENCODING, VCD_FUNCTION_IN);
|
||||
|
||||
nrLDPC_TB_encoding_parameters_t TBs[nb_ulsch];
|
||||
memset(TBs, 0, sizeof(TBs));
|
||||
nrLDPC_slot_encoding_parameters_t slot_parameters = {.frame = frame,
|
||||
.slot = slot,
|
||||
.nb_TBs = nb_ulsch,
|
||||
.threadPool = &get_nrUE_params()->Tpool,
|
||||
.tinput = NULL,
|
||||
.tprep = NULL,
|
||||
.tparity = NULL,
|
||||
.toutput = NULL,
|
||||
.TBs = TBs};
|
||||
|
||||
int max_num_segments = 0;
|
||||
for (uint_fast8_t pusch_id = 0; pusch_id < nb_ulsch; pusch_id++) {
|
||||
const uint8_t ULSCH_id = ULSCH_ids[pusch_id];
|
||||
const uint8_t harq_pid = ulsch[ULSCH_id].pusch_pdu.pusch_data.harq_process_id;
|
||||
NR_UL_UE_HARQ_t *harq_process = &ue->ul_harq_processes[harq_pid];
|
||||
max_num_segments = max(max_num_segments, harq_process->C);
|
||||
}
|
||||
|
||||
nrLDPC_segment_encoding_parameters_t segments[nb_ulsch][max_num_segments];
|
||||
memset(segments, 0, sizeof(segments));
|
||||
@@ -208,10 +198,25 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
|
||||
|
||||
nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[pusch_id];
|
||||
NR_UL_UE_HARQ_t *harq_process = &ue->ul_harq_processes[harq_pid];
|
||||
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &ulsch[ULSCH_id].pusch_pdu;
|
||||
const uint16_t nb_rb = pusch_pdu->rb_size;
|
||||
TB_parameters->harq_unique_pid = 2 * harq_pid + ULSCH_id;
|
||||
TB_parameters->C = harq_process->C;
|
||||
TB_parameters->K = harq_process->K;
|
||||
TB_parameters->Z = harq_process->Z;
|
||||
TB_parameters->F = harq_process->F;
|
||||
TB_parameters->BG = harq_process->BG;
|
||||
TB_parameters->Kb = harq_process->Kb;
|
||||
TB_parameters->nb_rb = nb_rb;
|
||||
TB_parameters->Qm = pusch_pdu->qam_mod_order;
|
||||
TB_parameters->mcs = pusch_pdu->mcs_index;
|
||||
TB_parameters->nb_layers = pusch_pdu->nrOfLayers;
|
||||
TB_parameters->rv_index = pusch_pdu->pusch_data.rv_index;
|
||||
TB_parameters->G = G[pusch_id];
|
||||
TB_parameters->tbslbrm = pusch_pdu->tbslbrm;
|
||||
TB_parameters->A = pusch_pdu->pusch_data.tb_size / 8;
|
||||
TB_parameters->segments = segments[pusch_id];
|
||||
|
||||
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &ulsch->pusch_pdu;
|
||||
uint16_t nb_rb = pusch_pdu->rb_size;
|
||||
memset(harq_process->f, 0, 14 * nb_rb * 12 * 16);
|
||||
TB_parameters->output = harq_process->f;
|
||||
|
||||
|
||||
@@ -49,6 +49,12 @@
|
||||
#include "PHY/NR_REFSIG/ul_ref_seq_nr.h"
|
||||
#include <openair2/UTIL/OPT/opt.h>
|
||||
#include "PHY/log_tools.h"
|
||||
#include "PHY/NR_UE_TRANSPORT/pucch_nr.h"
|
||||
#include <math.h>
|
||||
|
||||
#define MAX_RE_PER_SYMBOL_IN_ALLOC (275 * 12)
|
||||
#define MAX_NLQM (4 * 8)
|
||||
#define MAX_UCI_CODED_BITS 1024
|
||||
|
||||
//#define DEBUG_PUSCH_MAPPING
|
||||
//#define DEBUG_MAC_PDU
|
||||
@@ -56,33 +62,72 @@
|
||||
|
||||
//extern int32_t uplink_counter;
|
||||
|
||||
void nr_pusch_codeword_scrambling_uci(uint8_t *in, uint32_t size, uint32_t Nid, uint32_t n_RNTI, uint32_t* out)
|
||||
static void nr_pusch_codeword_scrambling_uci(uint8_t *in,
|
||||
uint32_t size,
|
||||
uint32_t Nid,
|
||||
uint32_t n_RNTI,
|
||||
const uci_on_pusch_bit_type_t *template,
|
||||
uint32_t *out)
|
||||
{
|
||||
uint32_t *seq = gold_cache((n_RNTI << 15) + Nid, (size + 31) / 32);
|
||||
for (int i=0; i<size; i++) {
|
||||
int idx = i / 32;
|
||||
int b_idx = i % 32;
|
||||
if (in[i]==NR_PUSCH_x)
|
||||
out[idx] ^= 1 << b_idx;
|
||||
else if (in[i]==NR_PUSCH_y){
|
||||
if (b_idx)
|
||||
out[idx] ^= (out[idx] & (1 << (b_idx - 1))) << 1;
|
||||
else{
|
||||
uint32_t temp_out = out[idx - 1];
|
||||
out[idx] ^= temp_out >> 31;
|
||||
uint32_t num_words = (size + 31) / 32;
|
||||
|
||||
// Step 1: Initial general scrambling
|
||||
// First convert unpacked input to bit-packed words
|
||||
uint32_t in_words[num_words];
|
||||
memset(in_words, 0, num_words * sizeof(uint32_t));
|
||||
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
uint32_t word_idx = i / 32;
|
||||
uint32_t bit_idx = i % 32;
|
||||
if (in[i] & 1) {
|
||||
in_words[word_idx] |= (1U << bit_idx);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < num_words; i++) {
|
||||
out[i] = in_words[i] ^ seq[i];
|
||||
}
|
||||
|
||||
// According to 38.211 6.3.1.1
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
if (template[i] == BIT_TYPE_ACK_ULSCH) {
|
||||
// Step 2: Overwrite/Correct positions for UCI bits including placeholders X, Y when O_ACK <= 2
|
||||
uint32_t pos = i;
|
||||
uint32_t idx = pos / 32;
|
||||
uint32_t b_idx = pos % 32;
|
||||
|
||||
if (in[pos] == NR_PUSCH_y) {
|
||||
// Clear bit
|
||||
out[idx] &= ~(1U << b_idx);
|
||||
if (b_idx > 0) {
|
||||
// Y depends on the final value of the previous bit in the same word.
|
||||
// This previous bit could be an ACK (already corrected) or ULSCH (from initial scramble).
|
||||
out[idx] |= ((out[idx] >> (b_idx - 1)) & 1) << b_idx;
|
||||
} else if (idx > 0) {
|
||||
// Y depends on the last bit of the previous word.
|
||||
out[idx] |= ((out[idx - 1] >> 31) & 1);
|
||||
}
|
||||
} else if (in[pos] == NR_PUSCH_x) {
|
||||
out[idx] |= (1U << b_idx);
|
||||
}
|
||||
}
|
||||
else
|
||||
out[idx] ^= (((in[i]) & 1) ^ ((seq[idx] >> b_idx) & 1)) << b_idx;
|
||||
//printf("i %d b_idx %d in %d s 0x%08x out 0x%08x\n", i, b_idx, in[i], s, *out);
|
||||
}
|
||||
}
|
||||
|
||||
void nr_pusch_codeword_scrambling(uint8_t *in, uint32_t size, uint32_t Nid, uint32_t n_RNTI, bool uci_on_pusch, uint32_t* out)
|
||||
void nr_pusch_codeword_scrambling(uint8_t *in,
|
||||
uint32_t size,
|
||||
uint32_t Nid,
|
||||
uint32_t n_RNTI,
|
||||
bool uci_on_pusch,
|
||||
const uci_on_pusch_bit_type_t *template,
|
||||
uint32_t *out)
|
||||
{
|
||||
if (uci_on_pusch)
|
||||
nr_pusch_codeword_scrambling_uci(in, size, Nid, n_RNTI, out);
|
||||
// in buffer is in byte-packed format
|
||||
nr_pusch_codeword_scrambling_uci(in, size, Nid, n_RNTI, template, out);
|
||||
else
|
||||
// in buffer is in bit-packed format
|
||||
nr_codeword_scrambling(in, size, 0, Nid, n_RNTI, out);
|
||||
}
|
||||
|
||||
@@ -464,6 +509,500 @@ static void map_symbols(const nr_phy_pxsch_params_t p,
|
||||
}
|
||||
}
|
||||
|
||||
// Function to lookup beta offset value from Table 9.3-1 in TS 38.213
|
||||
static double get_beta_offset_harq_ack(uint8_t beta_offset_index)
|
||||
{
|
||||
static const double beta_offset_values[21] = {
|
||||
1.000, // Index 0
|
||||
2.000, // Index 1
|
||||
2.500, // Index 2
|
||||
3.125, // Index 3
|
||||
4.000, // Index 4
|
||||
5.000, // Index 5
|
||||
6.250, // Index 6
|
||||
8.000, // Index 7
|
||||
10.000, // Index 8
|
||||
12.625, // Index 9
|
||||
15.875, // Index 10
|
||||
20.000, // Index 11
|
||||
31.000, // Index 12
|
||||
50.000, // Index 13
|
||||
80.000, // Index 14
|
||||
126.000, // Index 15
|
||||
0.6, // Index 16
|
||||
0.4, // Index 17
|
||||
0.2, // Index 18
|
||||
0.1, // Index 19
|
||||
0.05, // Index 20
|
||||
};
|
||||
|
||||
if (beta_offset_index > 20) {
|
||||
LOG_E(PHY, "Invalid beta_offset_index %d, using default value\n", beta_offset_index);
|
||||
return 20.000; // Default value using index 11
|
||||
}
|
||||
|
||||
return beta_offset_values[beta_offset_index];
|
||||
}
|
||||
|
||||
static double get_alpha_scaling_value(uint8_t alpha_scaling)
|
||||
{
|
||||
switch (alpha_scaling) {
|
||||
case 0:
|
||||
return 0.5;
|
||||
case 1:
|
||||
return 0.65;
|
||||
case 2:
|
||||
return 0.8;
|
||||
case 3:
|
||||
return 1.0;
|
||||
default:
|
||||
AssertFatal(false, "Invalid alpha_scaling value %d, valid range is 0-3", alpha_scaling);
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function gets the CRC size of UCI
|
||||
*/
|
||||
static int get_crc_uci(const uint16_t ouci)
|
||||
{
|
||||
int L = 0;
|
||||
if (ouci > 19) {
|
||||
L = 11;
|
||||
} else if (ouci > 11) {
|
||||
L = 6;
|
||||
} else {
|
||||
L = 0; // no ACK/NACK
|
||||
}
|
||||
|
||||
return L;
|
||||
}
|
||||
|
||||
static uint16_t get_Qd(const uint16_t oack, double beta, double alpha, const uint32_t sumKr, const uint32_t s1, const uint32_t s2)
|
||||
{
|
||||
if (oack == 0)
|
||||
return 0;
|
||||
|
||||
uint16_t first_term = ceil(((double)oack + get_crc_uci(oack)) * (double)beta * s1 / sumKr);
|
||||
uint16_t second_term = ceil(alpha * s2);
|
||||
|
||||
return (first_term < second_term) ? first_term : second_term;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function calculates the rate matching information for UCI multiplexing with PUSCH
|
||||
*/
|
||||
static rate_match_info_uci_t calc_rate_match_info_uci(const nfapi_nr_ue_pusch_pdu_t *pusch_pdu,
|
||||
const NR_UL_UE_HARQ_t *harq_process_ul_ue,
|
||||
const uint8_t nlqm,
|
||||
unsigned int *G)
|
||||
{
|
||||
// get beta offset
|
||||
uint8_t beta_offset_index = pusch_pdu->pusch_uci.beta_offset_harq_ack;
|
||||
double beta = get_beta_offset_harq_ack(beta_offset_index);
|
||||
|
||||
// get alpha scaling value
|
||||
uint8_t alpha_scaling = pusch_pdu->pusch_uci.alpha_scaling;
|
||||
double alpha = get_alpha_scaling_value(alpha_scaling);
|
||||
|
||||
// Calculate sumKr (total bits in all code blocks)
|
||||
uint32_t sumKr = 0;
|
||||
if (harq_process_ul_ue->C == 0) {
|
||||
sumKr = 0;
|
||||
} else if (harq_process_ul_ue->C == 1) {
|
||||
sumKr = harq_process_ul_ue->K;
|
||||
} else {
|
||||
sumKr = harq_process_ul_ue->K * harq_process_ul_ue->C;
|
||||
}
|
||||
|
||||
// Calculate s1: total number of non-DMRS REs in allocation
|
||||
uint16_t nb_rb = pusch_pdu->rb_size;
|
||||
uint8_t start_symbol = pusch_pdu->start_symbol_index;
|
||||
uint8_t number_of_symbols = pusch_pdu->nr_of_symbols;
|
||||
uint16_t ul_dmrs_symb_pos = pusch_pdu->ul_dmrs_symb_pos;
|
||||
|
||||
uint32_t s1 = 0;
|
||||
for (int l = start_symbol; l < start_symbol + number_of_symbols; l++) {
|
||||
if (!((ul_dmrs_symb_pos >> l) & 0x01)) {
|
||||
s1 += nb_rb * NR_NB_SC_PER_RB;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate s2: number of non-DMRS REs after first DMRS symbol
|
||||
int first_dmrs_symbol = -1;
|
||||
for (int l = start_symbol; l < start_symbol + number_of_symbols; l++) {
|
||||
if ((ul_dmrs_symb_pos >> l) & 0x01) {
|
||||
first_dmrs_symbol = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int l0 = -1;
|
||||
if (first_dmrs_symbol >= 0 && first_dmrs_symbol < start_symbol + number_of_symbols - 1) {
|
||||
l0 = first_dmrs_symbol + 1;
|
||||
}
|
||||
uint32_t s2 = 0;
|
||||
for (int l = l0; l < start_symbol + number_of_symbols; l++) {
|
||||
if (!((ul_dmrs_symb_pos >> l) & 0x01)) {
|
||||
s2 += nb_rb * NR_NB_SC_PER_RB;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t oack = pusch_pdu->pusch_uci.harq_ack_bit_length;
|
||||
uint16_t oack_rvd = (oack <= 2) ? 2 : 0; // get the reserved bits when oACK <= 2 according to TS 38.212 section 6.2.7, step 1
|
||||
|
||||
rate_match_info_uci_t rminfo = {0};
|
||||
|
||||
// get the number of coded HARQ-ACK symbols and bits, TS 38.212 section 6.3.2.4.1.1
|
||||
rminfo.Q_dash_ACK = get_Qd(oack, beta, alpha, sumKr, s1, s2);
|
||||
rminfo.E_uci_ACK = rminfo.Q_dash_ACK * nlqm;
|
||||
|
||||
if (oack_rvd > 0) {
|
||||
rminfo.Q_dash_ACK_rvd = get_Qd(oack_rvd, beta, alpha, sumKr, s1, s2);
|
||||
rminfo.E_uci_ACK_rvd = rminfo.Q_dash_ACK_rvd * nlqm;
|
||||
}
|
||||
|
||||
if (oack_rvd == 0) {
|
||||
rminfo.G_ulsch = *G - rminfo.E_uci_ACK;
|
||||
} else {
|
||||
rminfo.G_ulsch = *G;
|
||||
}
|
||||
|
||||
*G = rminfo.G_ulsch;
|
||||
LOG_D(PHY, "[UCI_RATE_MATCH] sumKr=%u, s1=%u, s2=%u, Final G_ulsch (output G): %u\n", sumKr, s1, s2, *G);
|
||||
LOG_D(PHY,
|
||||
"[UCI_RATE_MATCH] rate matching info returned: E_uci_ACK=%u, E_uci_ACK_rvd=%u, G_ulsch=%u\n",
|
||||
rminfo.E_uci_ACK,
|
||||
rminfo.E_uci_ACK_rvd,
|
||||
rminfo.G_ulsch);
|
||||
|
||||
return rminfo;
|
||||
}
|
||||
|
||||
static int initialize_mapping_resources(const nfapi_nr_ue_pusch_pdu_t *pusch_pdu,
|
||||
uint32_t *m_ulsch_initial,
|
||||
uint32_t *m_uci_current)
|
||||
{
|
||||
if (!pusch_pdu || !m_ulsch_initial || !m_uci_current)
|
||||
return -1;
|
||||
|
||||
const uint8_t n_pusch_sym_all = pusch_pdu->nr_of_symbols;
|
||||
const uint16_t ul_dmrs_symb_pos = pusch_pdu->ul_dmrs_symb_pos;
|
||||
const uint8_t dmrs_type = pusch_pdu->dmrs_config_type;
|
||||
const uint8_t cdm_grps_no_data = pusch_pdu->num_dmrs_cdm_grps_no_data;
|
||||
const uint32_t res_per_symbol_non_dmrs = pusch_pdu->rb_size * NR_NB_SC_PER_RB;
|
||||
const uint32_t data_re_on_dmrs_sym_per_prb = NR_NB_SC_PER_RB - get_num_dmrs_re_per_rb(dmrs_type, cdm_grps_no_data);
|
||||
|
||||
// Initialize resources per symbol for ULSCH and UCI
|
||||
for (uint8_t i = 0; i < n_pusch_sym_all; i++) {
|
||||
uint8_t absolute_symbol_idx = pusch_pdu->start_symbol_index + i;
|
||||
|
||||
if ((ul_dmrs_symb_pos >> absolute_symbol_idx) & 0x01) {
|
||||
// Calculate available data REs on DMRS symbols based on DMRS configuration
|
||||
|
||||
m_ulsch_initial[i] = pusch_pdu->rb_size * data_re_on_dmrs_sym_per_prb;
|
||||
m_uci_current[i] = 0; // UCI is not mapped on DMRS symbols
|
||||
|
||||
} else { // Not a DMRS symbol
|
||||
|
||||
m_ulsch_initial[i] = res_per_symbol_non_dmrs;
|
||||
m_uci_current[i] = res_per_symbol_non_dmrs;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_first_uci_symbol(const uint8_t start_symbol,
|
||||
const uint8_t num_symbols,
|
||||
const uint16_t dmrs_map,
|
||||
uint8_t *first_non_dmrs_sym,
|
||||
uint8_t *dmrs_p1)
|
||||
{
|
||||
// First non-DMRS symbol
|
||||
const uint16_t last_sym = start_symbol + num_symbols;
|
||||
for (uint_fast8_t s = start_symbol; s < last_sym; s++) {
|
||||
if (!is_dmrs_symbol(s, dmrs_map)) {
|
||||
*first_non_dmrs_sym = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Symbol after first consequtive DMRS symbol
|
||||
const uint8_t first_dmrs_sym = get_next_dmrs_symbol_in_slot(dmrs_map, start_symbol, last_sym);
|
||||
*dmrs_p1 = first_dmrs_sym + 1;
|
||||
while (is_dmrs_symbol(*dmrs_p1, dmrs_map) && *dmrs_p1 < last_sym) {
|
||||
(*dmrs_p1)++;
|
||||
}
|
||||
|
||||
// Return relative symbol idx
|
||||
*first_non_dmrs_sym -= start_symbol;
|
||||
*dmrs_p1 -= start_symbol;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function builds the initial template by reserving positions for HARQ-ACK.
|
||||
*/
|
||||
static void build_template_reserve_ack(uci_on_pusch_bit_type_t *template,
|
||||
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu,
|
||||
uint32_t G_ack_rvd,
|
||||
uint8_t l1_c,
|
||||
const uint32_t *m_uci_current,
|
||||
const uint32_t *m_ulsch_initial,
|
||||
uint32_t positions_by_sym[][MAX_UCI_CODED_BITS],
|
||||
uint32_t *count_by_sym)
|
||||
{
|
||||
const uint8_t n_symbols = pusch_pdu->nr_of_symbols;
|
||||
const uint32_t nlqm = pusch_pdu->qam_mod_order * pusch_pdu->nrOfLayers;
|
||||
|
||||
memset(count_by_sym, 0, n_symbols * sizeof(uint32_t));
|
||||
|
||||
uint32_t symbol_start_bit_idx[14] = {0};
|
||||
for (uint8_t s = 1; s < n_symbols; s++) {
|
||||
symbol_start_bit_idx[s] = symbol_start_bit_idx[s - 1] + (m_ulsch_initial[s - 1] * nlqm);
|
||||
}
|
||||
|
||||
// Reserve Positions using RE-level D-Factor Distribution
|
||||
uint32_t total_reserved = 0;
|
||||
|
||||
for (uint8_t sym = l1_c; sym < n_symbols && total_reserved < G_ack_rvd; sym++) {
|
||||
const uint32_t uci_re_on_sym = m_uci_current[sym];
|
||||
|
||||
if (uci_re_on_sym > 0) {
|
||||
const uint32_t remaining_to_reserve = G_ack_rvd - total_reserved;
|
||||
uint32_t d_factor_re;
|
||||
const uint32_t num_re_to_select = ceil((double)remaining_to_reserve / nlqm);
|
||||
if (num_re_to_select >= uci_re_on_sym) {
|
||||
d_factor_re = 1;
|
||||
} else {
|
||||
d_factor_re = floor((double)uci_re_on_sym / num_re_to_select);
|
||||
if (d_factor_re == 0) {
|
||||
d_factor_re = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t re_offset = 0; re_offset < uci_re_on_sym && total_reserved < G_ack_rvd; re_offset += d_factor_re) {
|
||||
for (uint32_t bit_in_re = 0; bit_in_re < nlqm; bit_in_re++) {
|
||||
if (total_reserved >= G_ack_rvd) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t bit_offset_in_sym = (re_offset * nlqm) + bit_in_re;
|
||||
uint32_t cw_idx = symbol_start_bit_idx[sym] + bit_offset_in_sym;
|
||||
template[cw_idx] = BIT_TYPE_ACK_RESERVED;
|
||||
positions_by_sym[sym][count_by_sym[sym]] = cw_idx;
|
||||
count_by_sym[sym]++;
|
||||
|
||||
total_reserved++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function maps the HARQ-ACK bits when O_ACK > 2
|
||||
*/
|
||||
static void map_non_overlapped_ack(uci_on_pusch_bit_type_t *template,
|
||||
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu,
|
||||
uint16_t G_ack,
|
||||
uint8_t l1_c,
|
||||
const uint32_t *m_uci_current,
|
||||
const uint32_t *m_ulsch_initial)
|
||||
{
|
||||
const uint8_t n_symbols = pusch_pdu->nr_of_symbols;
|
||||
const uint32_t nlqm = pusch_pdu->qam_mod_order * pusch_pdu->nrOfLayers;
|
||||
|
||||
uint32_t symbol_start_bit_idx[14] = {0};
|
||||
for (uint8_t s = 1; s < n_symbols; s++) {
|
||||
symbol_start_bit_idx[s] = symbol_start_bit_idx[s - 1] + (m_ulsch_initial[s - 1] * nlqm);
|
||||
}
|
||||
|
||||
uint32_t total_placed = 0;
|
||||
for (uint8_t sym = l1_c; sym < n_symbols && total_placed < G_ack; sym++) {
|
||||
const uint32_t uci_re_on_sym = m_uci_current[sym];
|
||||
|
||||
if (uci_re_on_sym > 0) {
|
||||
const uint32_t remaining_to_place = G_ack - total_placed;
|
||||
uint32_t d_factor_re;
|
||||
const uint32_t num_re_to_select = ceil((double)remaining_to_place / nlqm);
|
||||
|
||||
if (num_re_to_select >= uci_re_on_sym) {
|
||||
d_factor_re = 1;
|
||||
} else {
|
||||
d_factor_re = floor((double)uci_re_on_sym / num_re_to_select);
|
||||
if (d_factor_re == 0) {
|
||||
d_factor_re = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t re_offset = 0; re_offset < uci_re_on_sym && total_placed < G_ack; re_offset += d_factor_re) {
|
||||
for (uint32_t bit_in_re = 0; bit_in_re < nlqm; bit_in_re++) {
|
||||
if (total_placed >= G_ack) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t bit_offset_in_sym = (re_offset * nlqm) + bit_in_re;
|
||||
uint32_t cw_idx = symbol_start_bit_idx[sym] + bit_offset_in_sym;
|
||||
template[cw_idx] = BIT_TYPE_ACK;
|
||||
|
||||
total_placed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function maps the HARQ-ACK bits when O_ACK <= 2
|
||||
*/
|
||||
static void map_overlapped_ack(uci_on_pusch_bit_type_t *template,
|
||||
uint16_t G_ack,
|
||||
uint8_t l1_c,
|
||||
uint8_t n_symbols,
|
||||
uint32_t positions_by_sym[][MAX_UCI_CODED_BITS],
|
||||
const uint32_t *count_by_sym)
|
||||
{
|
||||
uint32_t ack_bits_marked = 0;
|
||||
|
||||
for (uint8_t sym_iter = l1_c; sym_iter < n_symbols && ack_bits_marked < G_ack; sym_iter++) {
|
||||
const uint32_t num_reserved_bits_on_sym = count_by_sym[sym_iter];
|
||||
|
||||
if (num_reserved_bits_on_sym > 0) {
|
||||
const uint32_t num_ack_remaining = G_ack - ack_bits_marked;
|
||||
uint32_t d_factor;
|
||||
|
||||
// This d-factor is calculated for stepping through the list of *reserved bits*.
|
||||
if (num_ack_remaining >= num_reserved_bits_on_sym) {
|
||||
d_factor = 1;
|
||||
} else {
|
||||
d_factor = floor((double)num_reserved_bits_on_sym / num_ack_remaining);
|
||||
if (d_factor == 0) {
|
||||
d_factor = 1;
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t *reserved_indices_on_this_sym = positions_by_sym[sym_iter];
|
||||
|
||||
for (uint32_t i = 0; i < num_reserved_bits_on_sym && ack_bits_marked < G_ack; i += d_factor) {
|
||||
uint32_t pos_to_mark = reserved_indices_on_this_sym[i];
|
||||
template[pos_to_mark] = BIT_TYPE_ACK_ULSCH;
|
||||
|
||||
ack_bits_marked++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Applies the template to build the final codeword
|
||||
*/
|
||||
static void apply_template_to_codeword(uint8_t *codeword,
|
||||
const uci_on_pusch_bit_type_t *template,
|
||||
uint32_t codeword_len,
|
||||
const uint8_t *ulsch_bits,
|
||||
const uint64_t *cack,
|
||||
uint16_t G_ack,
|
||||
uint32_t G_ulsch)
|
||||
{
|
||||
uint32_t ulsch_idx = 0;
|
||||
uint32_t ack_idx = 0;
|
||||
|
||||
for (uint32_t i = 0; i < codeword_len; i++) {
|
||||
switch (template[i]) {
|
||||
case BIT_TYPE_ACK:
|
||||
if (G_ack > 0 && ack_idx < G_ack) {
|
||||
uint32_t word_idx = ack_idx / 64;
|
||||
uint32_t bit_in_word_idx = ack_idx % 64;
|
||||
codeword[i] = (cack[word_idx] >> bit_in_word_idx) & 1;
|
||||
ack_idx++;
|
||||
}
|
||||
break;
|
||||
|
||||
case BIT_TYPE_ACK_ULSCH:
|
||||
if (G_ack > 0 && ack_idx < G_ack) {
|
||||
codeword[i] = ((const uint8_t *)cack)[ack_idx++];
|
||||
if (G_ulsch > 0 && ulsch_idx < G_ulsch) {
|
||||
ulsch_idx++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BIT_TYPE_ACK_RESERVED:
|
||||
case BIT_TYPE_ULSCH:
|
||||
default:
|
||||
if (G_ulsch > 0 && ulsch_idx < G_ulsch) {
|
||||
uint32_t byte_idx = ulsch_idx / 8;
|
||||
uint32_t bit_in_byte_idx = ulsch_idx % 8;
|
||||
codeword[i] = (ulsch_bits[byte_idx] >> bit_in_byte_idx) & 1;
|
||||
ulsch_idx++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function implements the UCI multiplexing on PUSCH according to TS 38.212 section 6.2.7.
|
||||
*/
|
||||
static uci_on_pusch_bit_type_t *nr_data_control_mapping(const nfapi_nr_ue_pusch_pdu_t *pusch_pdu,
|
||||
uci_on_pusch_bit_type_t *template,
|
||||
unsigned int G_ulsch,
|
||||
uint16_t G_ack,
|
||||
uint32_t G_ack_rvd,
|
||||
uint8_t *codeword,
|
||||
uint32_t codeword_len,
|
||||
const uint8_t *ulsch_bits,
|
||||
const uint64_t *cack)
|
||||
{
|
||||
if (!pusch_pdu || !codeword || codeword_len == 0 || !template)
|
||||
return NULL;
|
||||
const uint8_t n_symbols = pusch_pdu->nr_of_symbols;
|
||||
if (n_symbols == 0 || n_symbols > NR_NUMBER_OF_SYMBOLS_PER_SLOT)
|
||||
return NULL;
|
||||
|
||||
uint32_t m_ulsch_initial[NR_NUMBER_OF_SYMBOLS_PER_SLOT] = {0};
|
||||
uint32_t m_uci_current[NR_NUMBER_OF_SYMBOLS_PER_SLOT] = {0}; // This holds RE counts, not bit counts
|
||||
|
||||
if (initialize_mapping_resources(pusch_pdu, m_ulsch_initial, m_uci_current) != 0) {
|
||||
LOG_E(PHY, "Failed to initialize mapping resources\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t first_non_dmrs_sym = 0;
|
||||
uint8_t l1_c = 0;
|
||||
get_first_uci_symbol(pusch_pdu->start_symbol_index,
|
||||
pusch_pdu->nr_of_symbols,
|
||||
pusch_pdu->ul_dmrs_symb_pos,
|
||||
&first_non_dmrs_sym,
|
||||
&l1_c);
|
||||
|
||||
memset(template, 0, codeword_len * sizeof(uci_on_pusch_bit_type_t));
|
||||
|
||||
uint32_t positions_by_sym[NR_NUMBER_OF_SYMBOLS_PER_SLOT][MAX_UCI_CODED_BITS] = {0};
|
||||
uint32_t count_by_sym[NR_NUMBER_OF_SYMBOLS_PER_SLOT] = {0};
|
||||
|
||||
if (G_ack_rvd > 0) {
|
||||
build_template_reserve_ack(template,
|
||||
pusch_pdu,
|
||||
G_ack_rvd,
|
||||
l1_c,
|
||||
m_uci_current,
|
||||
m_ulsch_initial,
|
||||
positions_by_sym,
|
||||
count_by_sym);
|
||||
} else if (G_ack > 0) {
|
||||
map_non_overlapped_ack(template, pusch_pdu, G_ack, l1_c, m_uci_current, m_ulsch_initial);
|
||||
}
|
||||
|
||||
if (G_ack > 0 && G_ack_rvd > 0) {
|
||||
map_overlapped_ack(template, G_ack, l1_c, n_symbols, positions_by_sym, count_by_sym);
|
||||
}
|
||||
|
||||
apply_template_to_codeword(codeword, template, codeword_len, ulsch_bits, cack, G_ack, G_ulsch);
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
|
||||
const uint32_t frame,
|
||||
const uint8_t slot,
|
||||
@@ -486,8 +1025,11 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
|
||||
LOG_D(PHY, "nr_ue_ulsch_procedures_slot hard_id %d %d.%d prepare for coding\n", harq_pid, frame, slot);
|
||||
|
||||
NR_UE_ULSCH_t *ulsch_ue = &phy_data->ulsch;
|
||||
NR_UE_PUCCH *pucch_ue = &phy_data->pucch_vars;
|
||||
NR_UL_UE_HARQ_t *harq_process_ul_ue = &UE->ul_harq_processes[harq_pid];
|
||||
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu = &ulsch_ue->pusch_pdu;
|
||||
const fapi_nr_ul_config_pucch_pdu *pucch_pdu = &pucch_ue->pucch_pdu[0];
|
||||
uci_on_pusch_bit_type_t *uci_mapping_template = NULL;
|
||||
|
||||
uint16_t number_dmrs_symbols = 0;
|
||||
|
||||
@@ -528,6 +1070,11 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
|
||||
|
||||
G[pusch_id] = nr_get_G(nb_rb, number_of_symbols, nb_dmrs_re_per_rb, number_dmrs_symbols, unav_res, mod_order, Nl);
|
||||
|
||||
// Capture the initial total PUSCH bits. This is the total_codeword_length for mapping.
|
||||
unsigned int G_initial_total_pusch_bits = G[pusch_id];
|
||||
|
||||
uci_on_pusch_bit_type_t template_buffer[G_initial_total_pusch_bits];
|
||||
|
||||
ws_trace_t tmp = {.nr = true,
|
||||
.direction = DIRECTION_UPLINK,
|
||||
.pdu_buffer = harq_process_ul_ue->payload_AB,
|
||||
@@ -544,6 +1091,17 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
|
||||
|
||||
/////////////////////////ULSCH coding/////////////////////////
|
||||
|
||||
rate_match_info_uci_t rm_info = {0};
|
||||
const uint8_t nl_qm = Nl * mod_order; // product of number of layers and modulation order
|
||||
if(nr_ulsch_pre_encoding(UE, &phy_data->ulsch, frame, slot, G, 1, ULSCH_ids) != 0) {
|
||||
LOG_E(PHY, "Error pre-encoding\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pusch_pdu->pusch_uci.harq_ack_bit_length != 0) {
|
||||
rm_info = calc_rate_match_info_uci(pusch_pdu, harq_process_ul_ue, nl_qm, &G[pusch_id]);
|
||||
}
|
||||
|
||||
if (nr_ulsch_encoding(UE, &phy_data->ulsch, frame, slot, G, 1, ULSCH_ids, number_dmrs_symbols) == -1) {
|
||||
stop_meas_nr_ue_phy(UE, PUSCH_PROC_STATS);
|
||||
return;
|
||||
@@ -557,9 +1115,60 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
|
||||
|
||||
int N_PRB_oh = 0; // higher layer (RRC) parameter xOverhead in PUSCH-ServingCellConfig
|
||||
|
||||
AssertFatal(pusch_pdu->pusch_uci.harq_ack_bit_length == 0 && pusch_pdu->pusch_uci.csi_part1_bit_length == 0
|
||||
&& pusch_pdu->pusch_uci.csi_part2_bit_length == 0,
|
||||
"UCI on PUSCH not supported at PHY\n");
|
||||
if (pusch_pdu->pusch_uci.harq_ack_bit_length != 0) {
|
||||
LOG_D(PHY, "[UCI_ON_PUSCH] Original HARQ-ACK bit length: %u\n", pusch_pdu->pusch_uci.harq_ack_bit_length);
|
||||
LOG_D(PHY, "[UCI_ON_PUSCH] Initial G: %u\n", G_initial_total_pusch_bits);
|
||||
// b is the block of bits transmitted on the physical channel after payload coding
|
||||
uint64_t b[16] = {0}; // limit to 1024-bit encoded length
|
||||
|
||||
if (pucch_pdu == NULL) {
|
||||
LOG_E(PHY, "nr_ue_ulsch_procedures: pucch_pdu is NULL but HARQ-ACK is present. Cannot proceed with UCI encoding.\n");
|
||||
stop_meas_nr_ue_phy(UE, PUSCH_PROC_STATS);
|
||||
return;
|
||||
}
|
||||
|
||||
nr_uci_encoding(pusch_pdu->pusch_uci.harq_payload,
|
||||
pusch_pdu->pusch_uci.harq_ack_bit_length,
|
||||
pucch_pdu->prb_size,
|
||||
true,
|
||||
rm_info.E_uci_ACK,
|
||||
mod_order,
|
||||
&b[0]);
|
||||
|
||||
LOG_D(PHY,
|
||||
"[UCI_ON_PUSCH] G_ulsch=%u (updated G[pusch_id]), G_ack=%u (M_bit), G_ack_rvd=%u, total_len=%u "
|
||||
"(G_initial_total_pusch_bits).\n",
|
||||
G[pusch_id],
|
||||
rm_info.E_uci_ACK,
|
||||
rm_info.E_uci_ACK_rvd,
|
||||
G_initial_total_pusch_bits);
|
||||
|
||||
uint8_t *temp_codeword = malloc(G_initial_total_pusch_bits * sizeof(uint8_t));
|
||||
if (!temp_codeword) {
|
||||
LOG_E(PHY, "[UCI_ON_PUSCH] Failed to allocate memory for temporary codeword\n");
|
||||
uci_mapping_template = NULL;
|
||||
} else {
|
||||
start_meas_nr_ue_phy(UE, UCI_ON_PUSCH_MAPPING);
|
||||
nr_data_control_mapping(pusch_pdu,
|
||||
template_buffer,
|
||||
G[pusch_id],
|
||||
rm_info.E_uci_ACK,
|
||||
rm_info.E_uci_ACK_rvd,
|
||||
temp_codeword,
|
||||
G_initial_total_pusch_bits,
|
||||
harq_process_ul_ue->f,
|
||||
b);
|
||||
stop_meas_nr_ue_phy(UE, UCI_ON_PUSCH_MAPPING);
|
||||
|
||||
memcpy(harq_process_ul_ue->f, temp_codeword, G_initial_total_pusch_bits);
|
||||
free(temp_codeword);
|
||||
|
||||
uci_mapping_template = template_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
AssertFatal(pusch_pdu->pusch_uci.csi_part1_bit_length == 0 && pusch_pdu->pusch_uci.csi_part2_bit_length == 0,
|
||||
"UCI (CSI) on PUSCH not supported at PHY\n");
|
||||
|
||||
uint16_t start_rb = pusch_pdu->rb_start;
|
||||
uint16_t start_sc = frame_parms->first_carrier_offset + (start_rb + pusch_pdu->bwp_start) * NR_NB_SC_PER_RB;
|
||||
@@ -592,17 +1201,32 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
|
||||
|
||||
/////////////////////////ULSCH scrambling/////////////////////////
|
||||
|
||||
uint32_t available_bits = G[pusch_id];
|
||||
// +1 because size can be not modulo 4
|
||||
uint32_t scrambled_output[available_bits / (8 * sizeof(uint32_t)) + 1];
|
||||
uint32_t available_bits;
|
||||
bool is_uci_on_pusch = (pusch_pdu->pusch_uci.harq_ack_bit_length != 0);
|
||||
|
||||
if (is_uci_on_pusch) {
|
||||
// UCI on PUSCH is present, so available bits are the total codeword length
|
||||
available_bits = G_initial_total_pusch_bits;
|
||||
} else {
|
||||
// No UCI on PUSCH, so available bits are the initial G value
|
||||
available_bits = G[pusch_id];
|
||||
}
|
||||
|
||||
// +1 because size can be not modulo 4 for the uint32_t array
|
||||
uint32_t scrambled_output_len_u32 = (available_bits + 31) / 32; // Round up to nearest uint32_t count
|
||||
uint32_t scrambled_output[scrambled_output_len_u32];
|
||||
memset(scrambled_output, 0, sizeof(scrambled_output));
|
||||
|
||||
nr_pusch_codeword_scrambling(harq_process_ul_ue->f,
|
||||
available_bits,
|
||||
pusch_pdu->data_scrambling_id,
|
||||
rnti,
|
||||
false,
|
||||
is_uci_on_pusch,
|
||||
uci_mapping_template,
|
||||
scrambled_output);
|
||||
if (UE->phy_sim_test_buf) {
|
||||
memcpy(UE->phy_sim_test_buf, scrambled_output, (available_bits + 7) / 8);
|
||||
}
|
||||
#if T_TRACER
|
||||
if (T_ACTIVE(T_UE_PHY_UL_SCRAMBLED_TX_BITS)) {
|
||||
// Get Time Stamp for T-tracer messages
|
||||
@@ -885,19 +1509,21 @@ uint8_t nr_ue_pusch_common_procedures(PHY_VARS_NR_UE *UE,
|
||||
c16_t **txdataF,
|
||||
c16_t **txdata,
|
||||
uint32_t linktype,
|
||||
bool was_symbol_used[NR_NUMBER_OF_SYMBOLS_PER_SLOT])
|
||||
bool was_symbol_used[NR_NUMBER_OF_SYMBOLS_PER_SLOT],
|
||||
bool no_phase_pre_comp)
|
||||
{
|
||||
int N_RB = (linktype == link_type_sl) ? frame_parms->N_RB_SL : frame_parms->N_RB_UL;
|
||||
|
||||
for (int i = 0; i < NR_NUMBER_OF_SYMBOLS_PER_SLOT; i++) {
|
||||
if (was_symbol_used[i] == false)
|
||||
continue;
|
||||
for (int ap = 0; ap < n_antenna_ports; ap++) {
|
||||
apply_nr_rotation_TX(frame_parms, txdataF[ap], frame_parms->symbol_rotation[linktype], slot, N_RB, i, 1);
|
||||
if (!no_phase_pre_comp) {
|
||||
for (int i = 0; i < NR_NUMBER_OF_SYMBOLS_PER_SLOT; i++) {
|
||||
if (was_symbol_used[i] == false)
|
||||
continue;
|
||||
for (int ap = 0; ap < n_antenna_ports; ap++) {
|
||||
apply_nr_rotation_TX(frame_parms, txdataF[ap], frame_parms->symbol_rotation[linktype], slot, N_RB, i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int ap = 0; ap < n_antenna_ports; ap++) {
|
||||
if (frame_parms->Ncp == 1) { // extended cyclic prefix
|
||||
for (int i = 0; i < NR_NUMBER_OF_SYMBOLS_PER_SLOT_EXTENDED_CP; i++) {
|
||||
|
||||
@@ -49,6 +49,11 @@
|
||||
#define DEBUG_NR_PUCCH_TX
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
// #define DEBUG_UCI_ACK
|
||||
|
||||
//#define ONE_OVER_SQRT2 23170 // 32767/sqrt(2) = 23170 (ONE_OVER_SQRT2)
|
||||
//#define POLAR_CODING_DEBUG
|
||||
|
||||
@@ -485,6 +490,45 @@ void nr_generate_pucch1(const PHY_VARS_NR_UE *ue,
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate number of DMRS symbols for PUCCH formats 3 and 4
|
||||
static uint8_t nr_pucch_get_dmrs_symbols(uint8_t nrofSymbols, uint8_t add_dmrs)
|
||||
{
|
||||
if (nrofSymbols == 4) {
|
||||
return 1;
|
||||
} else if (nrofSymbols > 4 && nrofSymbols <= 9) {
|
||||
return 2;
|
||||
} else if (nrofSymbols > 9) {
|
||||
return (add_dmrs == 0) ? 2 : 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate PUCCH format 2/3/4 rate matching output sequence length according to TS 38.212 Table 6.3.1.4-1
|
||||
static uint16_t nr_pucch_output_sequence_length(uint8_t format_type,
|
||||
uint8_t nrofSymbols,
|
||||
uint16_t nrofPRB,
|
||||
uint8_t n_SF_PUCCH_s,
|
||||
uint8_t is_pi_over_2_bpsk_enabled,
|
||||
uint8_t add_dmrs)
|
||||
{
|
||||
uint16_t M_bit = 0;
|
||||
|
||||
if (format_type == 2) {
|
||||
M_bit = 16 * nrofSymbols * nrofPRB;
|
||||
} else if (format_type == 3) {
|
||||
uint16_t E_init = (is_pi_over_2_bpsk_enabled == 0) ? 24 : 12;
|
||||
uint8_t num_dmrs_symbols = nr_pucch_get_dmrs_symbols(nrofSymbols, add_dmrs);
|
||||
M_bit = E_init * (nrofSymbols - num_dmrs_symbols) * nrofPRB / n_SF_PUCCH_s;
|
||||
} else if (format_type == 4) {
|
||||
nrofPRB = 1;
|
||||
uint16_t E_init = (is_pi_over_2_bpsk_enabled == 0) ? 24 : 12;
|
||||
uint8_t num_dmrs_symbols = nr_pucch_get_dmrs_symbols(nrofSymbols, add_dmrs);
|
||||
M_bit = E_init * (nrofSymbols - num_dmrs_symbols) * nrofPRB / n_SF_PUCCH_s;
|
||||
}
|
||||
|
||||
return M_bit;
|
||||
}
|
||||
|
||||
static inline void nr_pucch2_3_4_scrambling(uint16_t M_bit, uint16_t rnti, uint16_t n_id, uint64_t *B64, uint8_t *btilde)
|
||||
{
|
||||
// c_init=nRNTI*2^15+n_id according to TS 38.211 Subclause 6.3.2.6.1
|
||||
@@ -515,101 +559,143 @@ static inline void nr_pucch2_3_4_scrambling(uint16_t M_bit, uint16_t rnti, uint1
|
||||
printf("\t\t [nr_pucch2_3_4_scrambling] scrambling M_bit=%d bits\n", M_bit);
|
||||
#endif
|
||||
}
|
||||
static void nr_uci_encoding(uint64_t payload,
|
||||
uint8_t nr_bit,
|
||||
int fmt,
|
||||
uint8_t is_pi_over_2_bpsk_enabled,
|
||||
uint8_t nrofSymbols,
|
||||
uint8_t nrofPRB,
|
||||
uint8_t n_SF_PUCCH_s,
|
||||
uint8_t intraSlotFrequencyHopping,
|
||||
uint8_t add_dmrs,
|
||||
uint64_t *b,
|
||||
uint16_t *M_bit) {
|
||||
|
||||
void nr_uci_encoding(uint64_t payload, uint8_t nr_bit, uint8_t nrofPRB, bool uci_on_pusch, uint16_t E, uint8_t Qm, uint64_t *b)
|
||||
{
|
||||
/*
|
||||
* Implementing TS 38.212 Subclause 6.3.1.2
|
||||
* Implementing TS 38.212 Subclause 6.3.1.2 and 6.3.2
|
||||
*
|
||||
*/
|
||||
// A is the payload size, to be provided in function call
|
||||
uint8_t A = nr_bit;
|
||||
// L is the CRC size
|
||||
//uint8_t L;
|
||||
// uint8_t L;
|
||||
// E is the rate matching output sequence length as given in TS 38.212 subclause 6.3.1.4.1
|
||||
uint16_t E=0,E_init;
|
||||
|
||||
if (fmt == 2) E = 16*nrofSymbols*nrofPRB;
|
||||
|
||||
if (fmt == 3) {
|
||||
E_init = (is_pi_over_2_bpsk_enabled == 0) ? 24:12;
|
||||
|
||||
if (nrofSymbols == 4) {
|
||||
E = (intraSlotFrequencyHopping == 0)?(E_init*(nrofSymbols-1)*nrofPRB):((E_init*(nrofSymbols-1)*nrofPRB));
|
||||
// int I_seg;
|
||||
#ifdef DEBUG_NR_PUCCH_TX
|
||||
printf("format 3 nrofSymbols =4 and E_init=%d,E=%d\n",E_init,E);
|
||||
printf("\t\t [nr_uci_encoding] start function with encoding A=%d bits into M_bit=%d (where nrofPRB=%d)\n", A, E, nrofPRB);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (nrofSymbols > 4) {
|
||||
E = E_init*(nrofSymbols-2)*nrofPRB;
|
||||
#ifdef DEBUG_NR_PUCCH_TX
|
||||
printf("format 3 nrofSymbols >4 and E_init=%d,E = %d\n",E_init,E);
|
||||
#endif
|
||||
}
|
||||
// For A=1 case (single bit UCI)
|
||||
if (A == 1) {
|
||||
uint8_t uci_bit_val = payload & 1; // Extract the single UCI bit
|
||||
uint64_t pattern_word;
|
||||
uint8_t *b_bytes = (uint8_t *)b; // Access b as bytes to set individual special values
|
||||
|
||||
if (nrofSymbols > 9) {
|
||||
E = (add_dmrs == 0)?(E_init*(nrofSymbols-2)*nrofPRB):((E_init*(nrofSymbols-4)*nrofPRB));
|
||||
#ifdef DEBUG_NR_PUCCH_TX
|
||||
printf("format 3 nrofSymbols >9 and E_init=%d,E = %d\n",E_init,E);
|
||||
#endif
|
||||
if (Qm == 1) {
|
||||
// For BPSK, just repeat the input bit
|
||||
pattern_word = uci_bit_val ? 0xFFFFFFFFFFFFFFFFULL : 0x0000000000000000ULL;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
b[i] = pattern_word;
|
||||
}
|
||||
} else {
|
||||
// For higher order modulation (QPSK, etc.), use placeholder values
|
||||
memset(b, 0, 8 * sizeof(uint64_t));
|
||||
|
||||
// Fill the entire output with the pattern
|
||||
for (int i = 0; i < E; i++) {
|
||||
switch (i % Qm) {
|
||||
case 0:
|
||||
// First bit in each group is actual UCI bit
|
||||
b_bytes[i] = uci_bit_val;
|
||||
LOG_D(PHY,
|
||||
"[UCI_ENCODING_A1_QAM_LOOP] i=%d, i%%Qm=%d (case 0), writing uci_bit_val %d to b_bytes[%d]\n",
|
||||
i,
|
||||
i % Qm,
|
||||
uci_bit_val,
|
||||
i);
|
||||
break;
|
||||
case 1:
|
||||
b_bytes[i] = NR_PUSCH_y;
|
||||
LOG_D(PHY,
|
||||
"[UCI_ENCODING_A1_QAM_LOOP] i=%d, i%%Qm=%d (case 1), writing NR_PUSCH_y (0x%02X) to b_bytes[%d]\n",
|
||||
i,
|
||||
i % Qm,
|
||||
NR_PUSCH_y,
|
||||
i);
|
||||
break;
|
||||
default:
|
||||
b_bytes[i] = NR_PUSCH_x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// For A=2 case (two bits UCI)
|
||||
else if (A == 2) {
|
||||
uint8_t bit0 = (payload >> 0) & 1;
|
||||
uint8_t bit1 = (payload >> 1) & 1;
|
||||
uint8_t c2 = bit0 ^ bit1; // Parity bit (XOR of the two bits)
|
||||
uint8_t *b_bytes = (uint8_t *)b;
|
||||
|
||||
if (fmt == 4) {
|
||||
E_init = (is_pi_over_2_bpsk_enabled == 0) ? 24:12;
|
||||
if (Qm == 1) {
|
||||
// For BPSK, output is [bit0, bit1, c2]
|
||||
uint64_t pattern = (bit0) | (bit1 << 1) | (c2 << 2);
|
||||
// Repeat this 3-bit pattern to fill the output
|
||||
pattern |= pattern << 3;
|
||||
pattern |= pattern << 6;
|
||||
pattern |= pattern << 12;
|
||||
pattern |= pattern << 24;
|
||||
pattern |= pattern << 48;
|
||||
|
||||
if (nrofSymbols == 4) {
|
||||
E = (intraSlotFrequencyHopping == 0)?(E_init*(nrofSymbols-1)/n_SF_PUCCH_s):((E_init*(nrofSymbols-1)/n_SF_PUCCH_s));
|
||||
#ifdef DEBUG_NR_PUCCH_TX
|
||||
printf("format 4 nrofSymbols =4 and E_init=%d,E=%d\n",E_init,E);
|
||||
#endif
|
||||
for (int i = 0; i < 8; i++) {
|
||||
b[i] = pattern;
|
||||
}
|
||||
} else {
|
||||
// For higher order modulation (Qm>=2), using patterns from Table 5.3.3.2-1
|
||||
// Pattern: 3 groups of Qm bits each = 3*Qm total length
|
||||
memset(b, 0, 8 * sizeof(uint64_t));
|
||||
|
||||
for (int i = 0; i < E; i++) {
|
||||
int pos_in_pattern = i % (3 * Qm);
|
||||
int group = pos_in_pattern / Qm;
|
||||
int pos_in_group = pos_in_pattern % Qm;
|
||||
uint8_t val_to_write;
|
||||
|
||||
if (pos_in_group == 0) {
|
||||
if (group == 0)
|
||||
val_to_write = bit0;
|
||||
else if (group == 1)
|
||||
val_to_write = c2;
|
||||
else
|
||||
val_to_write = bit1;
|
||||
} else if (pos_in_group == 1) {
|
||||
if (group == 0)
|
||||
val_to_write = bit1;
|
||||
else if (group == 1)
|
||||
val_to_write = bit0;
|
||||
else
|
||||
val_to_write = c2;
|
||||
} else {
|
||||
// Positions 2 and beyond are x placeholders
|
||||
val_to_write = NR_PUSCH_x;
|
||||
}
|
||||
|
||||
b_bytes[i] = val_to_write;
|
||||
}
|
||||
}
|
||||
|
||||
if (nrofSymbols > 4) {
|
||||
E = E_init*(nrofSymbols-2)/n_SF_PUCCH_s;
|
||||
#ifdef DEBUG_NR_PUCCH_TX
|
||||
printf("format 4 nrofSymbols >4 and E_init=%d,E = %d\n",E_init,E);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (nrofSymbols > 9) {
|
||||
E = (add_dmrs == 0)?(E_init*(nrofSymbols-2)/n_SF_PUCCH_s):((E_init*(nrofSymbols-4)/n_SF_PUCCH_s));
|
||||
#ifdef DEBUG_NR_PUCCH_TX
|
||||
printf("format 4 nrofSymbols >9 and E_init=%d,E = %d\n",E_init,E);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
*M_bit = E;
|
||||
//int I_seg;
|
||||
#ifdef DEBUG_NR_PUCCH_TX
|
||||
printf("\t\t [nr_uci_encoding] start function with fmt=%d, encoding A=%d bits into M_bit=%d (where nrofSymbols=%d,nrofPRB=%d)\n",fmt,A,*M_bit,nrofSymbols,nrofPRB);
|
||||
#endif
|
||||
|
||||
if (A<=11) {
|
||||
} else if (A <= 11) {
|
||||
// procedure in subclause 6.3.1.2.2 (UCI encoded by channel coding of small block lengths -> subclause 6.3.1.3.2)
|
||||
// CRC bits are not attached, and coding small block lengths (subclause 5.3.3)
|
||||
uint64_t b0 = encodeSmallBlock(payload, A);
|
||||
// repetition for rate-matching up to 16 PRB
|
||||
b[0] = b0 | (b0<<32);
|
||||
b[1] = b[0];
|
||||
b[2] = b[0];
|
||||
b[3] = b[0];
|
||||
b[4] = b[0];
|
||||
b[5] = b[0];
|
||||
b[6] = b[0];
|
||||
b[7] = b[0];
|
||||
AssertFatal(nrofPRB<=16,"Number of PRB >16\n");
|
||||
} else if (A>=12) {
|
||||
if (uci_on_pusch) {
|
||||
b[0] = b0;
|
||||
for (int i = 1; i < 8; i++) {
|
||||
b[i] = 0;
|
||||
}
|
||||
} else {
|
||||
// repetition for rate-matching up to 16 PRB
|
||||
b[0] = b0 | (b0<<32);
|
||||
b[1] = b[0];
|
||||
b[2] = b[0];
|
||||
b[3] = b[0];
|
||||
b[4] = b[0];
|
||||
b[5] = b[0];
|
||||
b[6] = b[0];
|
||||
b[7] = b[0];
|
||||
AssertFatal(nrofPRB<=16,"Number of PRB >16\n");
|
||||
}
|
||||
} else if (A >= 12) {
|
||||
// Encoder reversal
|
||||
payload = reverse_bits(payload, A);
|
||||
|
||||
@@ -618,7 +704,73 @@ static void nr_uci_encoding(uint64_t payload,
|
||||
A,
|
||||
nrofPRB);
|
||||
}
|
||||
|
||||
|
||||
if (uci_on_pusch) {
|
||||
// Rate matching for HARQ ACK following 38.212 section 5.4.3
|
||||
uint64_t output[8] = {0}; // Assuming max 512 bits (8 words of 64 bits)
|
||||
uint16_t N;
|
||||
if (nr_bit <= 2) {
|
||||
// For A=1 (BPSK), N=1. For A=2 (QPSK), N=3
|
||||
N = (nr_bit == 1) ? 1 : 3;
|
||||
} else if (nr_bit <= 11) {
|
||||
// For 3 <= A <= 11, the small block encoder produces a 32-bit codeword
|
||||
N = 32;
|
||||
} else {
|
||||
// For polar-coded UCI, output depends on nrofPRB
|
||||
N = 16 * nrofPRB;
|
||||
}
|
||||
|
||||
if ((nr_bit == 1 || nr_bit == 2) && Qm > 1) {
|
||||
LOG_D(PHY,
|
||||
"[UCI_ENCODING_RM] Bypassing bit-wise rate matching for A=%d, Qm=%d. 'b' (length %d bytes) is assumed to be already "
|
||||
"final.\n",
|
||||
nr_bit,
|
||||
Qm,
|
||||
E);
|
||||
} else {
|
||||
if (N == 0) {
|
||||
LOG_W(PHY, "HARQ-ACK rate matching with encoded_length=0 but E_uci_ack=%d\n", E);
|
||||
return;
|
||||
}
|
||||
|
||||
// Rate matching with single loop for both repetition and puncturing
|
||||
for (int i = 0; i < E; i++) {
|
||||
int src_bit = i % N; // Modulo for cyclic repetition
|
||||
int src_word = src_bit / 64;
|
||||
int src_bit_pos = src_bit % 64;
|
||||
int dst_word = i / 64;
|
||||
int dst_bit_pos = i % 64;
|
||||
|
||||
if ((b[src_word] >> src_bit_pos) & 1ULL)
|
||||
output[dst_word] |= (1ULL << dst_bit_pos);
|
||||
}
|
||||
|
||||
for (int i = 0; i < (E + 63) / 64; i++) {
|
||||
b[i] = output[i];
|
||||
}
|
||||
|
||||
#ifdef DEBUG_UCI_ACK
|
||||
LOG_I(PHY, "==== Final encoded UCI bits (E=%d) ====\n", E);
|
||||
int bit_count = E;
|
||||
|
||||
// Print in groups of 8 bits for readability
|
||||
for (int i = 0; i < (bit_count + 63) / 64; i++) {
|
||||
LOG_I(PHY, "Word %d: 0x%016lx\n", i, b[i]);
|
||||
|
||||
for (int j = 0; j < MIN(64, bit_count - i * 64); j += 8) {
|
||||
char bit_str[9] = {0}; // 8 bits + null terminator
|
||||
|
||||
for (int k = 0; k < MIN(8, bit_count - i * 64 - j); k++) {
|
||||
bit_str[k] = '0' + ((b[i] >> (j + k)) & 1);
|
||||
}
|
||||
|
||||
LOG_I(PHY, " Bits %3d-%3d: %s\n", i * 64 + j, MIN(i * 64 + j + 7, bit_count - 1), bit_str);
|
||||
}
|
||||
}
|
||||
LOG_I(PHY, "========================================\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
//#if 0
|
||||
void nr_generate_pucch2(const PHY_VARS_NR_UE *ue,
|
||||
@@ -634,13 +786,8 @@ void nr_generate_pucch2(const PHY_VARS_NR_UE *ue,
|
||||
// b is the block of bits transmitted on the physical channel after payload coding
|
||||
uint64_t b[16] = {0}; // limit to 1024-bit encoded length
|
||||
// M_bit is the number of bits of block b (payload after encoding)
|
||||
uint16_t M_bit = 0;
|
||||
nr_uci_encoding(pucch_pdu->payload,
|
||||
pucch_pdu->n_bit,
|
||||
2,0,
|
||||
pucch_pdu->nr_of_symbols,
|
||||
pucch_pdu->prb_size,
|
||||
1,0,0,&b[0],&M_bit);
|
||||
uint16_t M_bit = nr_pucch_output_sequence_length(pucch_pdu->format_type, pucch_pdu->nr_of_symbols, pucch_pdu->prb_size, 0, 0, 0);
|
||||
nr_uci_encoding(pucch_pdu->payload, pucch_pdu->n_bit, pucch_pdu->prb_size, false, M_bit, 0, &b[0]);
|
||||
/*
|
||||
* Implementing TS 38.211
|
||||
* Subclauses 6.3.2.5.1 Scrambling (PUCCH format 2)
|
||||
@@ -825,7 +972,7 @@ void nr_generate_pucch3_4(const PHY_VARS_NR_UE *ue,
|
||||
// b is the block of bits transmitted on the physical channel after payload coding
|
||||
uint64_t b[16];
|
||||
// M_bit is the number of bits of block b (payload after encoding)
|
||||
uint16_t M_bit;
|
||||
uint16_t M_bit = 0;
|
||||
// parameter PUCCH-F4-preDFT-OCC-length set of {2,4} -> to use table -1 or -2
|
||||
// in format 4, n_SF_PUCCH_s = {2,4}, provided by higher layer parameter PUCCH-F4-preDFT-OCC-length (in format 3 n_SF_PUCCH_s=1)
|
||||
uint8_t n_SF_PUCCH_s;
|
||||
@@ -852,17 +999,14 @@ void nr_generate_pucch3_4(const PHY_VARS_NR_UE *ue,
|
||||
uint16_t startingPRB = pucch_pdu->prb_start + pucch_pdu->bwp_start;
|
||||
uint8_t add_dmrs = pucch_pdu->add_dmrs_flag;
|
||||
|
||||
nr_uci_encoding(pucch_pdu->payload,
|
||||
pucch_pdu->n_bit,
|
||||
pucch_pdu->format_type,
|
||||
is_pi_over_2_bpsk_enabled,
|
||||
nrofSymbols,
|
||||
nrofPRB,
|
||||
n_SF_PUCCH_s,
|
||||
intraSlotFrequencyHopping,
|
||||
add_dmrs,
|
||||
b,
|
||||
&M_bit);
|
||||
M_bit = nr_pucch_output_sequence_length(pucch_pdu->format_type,
|
||||
nrofSymbols,
|
||||
nrofPRB,
|
||||
n_SF_PUCCH_s,
|
||||
is_pi_over_2_bpsk_enabled,
|
||||
add_dmrs);
|
||||
|
||||
nr_uci_encoding(pucch_pdu->payload, pucch_pdu->n_bit, nrofPRB, false, M_bit, 0, b);
|
||||
/*
|
||||
* Implementing TS 38.211
|
||||
* Subclauses 6.3.2.6.1 Scrambling (PUCCH formats 3 and 4)
|
||||
|
||||
@@ -72,6 +72,8 @@ void nr_generate_pucch3_4(const PHY_VARS_NR_UE *ue,
|
||||
const int nr_slot_tx,
|
||||
const fapi_nr_ul_config_pucch_pdu *pucch_pdu);
|
||||
|
||||
void nr_uci_encoding(uint64_t payload, uint8_t nr_bit, uint8_t nrofPRB, bool uci_on_pusch, uint16_t E, uint8_t Qm, uint64_t *b);
|
||||
|
||||
// tables for mcs values for different payloads
|
||||
static const uint8_t table1_mcs[]={0,6,3,9};
|
||||
static const uint8_t table2_mcs[]={0,3,9,6,1,4,10,7};
|
||||
|
||||
@@ -498,6 +498,9 @@ typedef struct PHY_VARS_NR_UE_s {
|
||||
/// RF and Interface devices per CC
|
||||
openair0_device rfdevice;
|
||||
|
||||
/// Phase precompensation flag
|
||||
bool no_phase_pre_comp;
|
||||
|
||||
void* scopeData;
|
||||
// Pointers to hold PDSCH data only for phy simulators
|
||||
void *phy_sim_rxdataF;
|
||||
@@ -507,6 +510,7 @@ typedef struct PHY_VARS_NR_UE_s {
|
||||
void *phy_sim_pdsch_dl_ch_estimates;
|
||||
void *phy_sim_pdsch_dl_ch_estimates_ext;
|
||||
uint8_t *phy_sim_dlsch_b;
|
||||
uint8_t *phy_sim_test_buf;
|
||||
|
||||
dynamic_barrier_t process_slot_tx_barriers[NUM_PROCESS_SLOT_TX_BARRIERS];
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
FN(DLSCH_PROCEDURES_STATS),\
|
||||
FN(PHY_PROC_TX),\
|
||||
FN(PUSCH_PROC_STATS),\
|
||||
FN(UCI_ON_PUSCH_MAPPING),\
|
||||
FN(ULSCH_SEGMENTATION_STATS),\
|
||||
FN(ULSCH_LDPC_ENCODING_STATS),\
|
||||
FN(ULSCH_RATE_MATCHING_STATS),\
|
||||
|
||||
@@ -310,7 +310,8 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, n
|
||||
(c16_t **)txdataF,
|
||||
txp,
|
||||
link_type_ul,
|
||||
was_symbol_used);
|
||||
was_symbol_used,
|
||||
ue->no_phase_pre_comp);
|
||||
stop_meas_nr_ue_phy(ue, OFDM_MOD_STATS);
|
||||
}
|
||||
|
||||
@@ -1112,13 +1113,7 @@ void pdsch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_
|
||||
}
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP_PDSCH, VCD_FUNCTION_OUT);
|
||||
|
||||
uint8_t nb_re_dmrs;
|
||||
if (dlsch_config->dmrsConfigType == NFAPI_NR_DMRS_TYPE1) {
|
||||
nb_re_dmrs = 6 * dlsch_config->n_dmrs_cdm_groups;
|
||||
}
|
||||
else {
|
||||
nb_re_dmrs = 4 * dlsch_config->n_dmrs_cdm_groups;
|
||||
}
|
||||
const uint8_t nb_re_dmrs = get_num_dmrs_re_per_rb(dlsch_config->dmrsConfigType, dlsch_config->n_dmrs_cdm_groups);
|
||||
uint16_t dmrs_len = get_num_dmrs(dlsch_config->dlDmrsSymbPos);
|
||||
uint32_t unav_res = 0;
|
||||
if(dlsch_config->pduBitmap & 0x1) {
|
||||
|
||||
@@ -313,7 +313,15 @@ void phy_procedures_nrUE_SL_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc
|
||||
was_symbol_used[i] = true;
|
||||
if (tx_action) {
|
||||
LOG_D(NR_PHY, "Sending Uplink data \n");
|
||||
nr_ue_pusch_common_procedures(ue, proc->nr_slot_tx, fp, fp->nb_antennas_tx, txdataF, txp, link_type_sl, was_symbol_used);
|
||||
nr_ue_pusch_common_procedures(ue,
|
||||
proc->nr_slot_tx,
|
||||
fp,
|
||||
fp->nb_antennas_tx,
|
||||
txdataF,
|
||||
txp,
|
||||
link_type_sl,
|
||||
was_symbol_used,
|
||||
ue->no_phase_pre_comp);
|
||||
}
|
||||
|
||||
LOG_D(NR_PHY, "****** end Sidelink TX-Chain for AbsSubframe %d.%d ******\n", frame_tx, slot_tx);
|
||||
|
||||
@@ -513,6 +513,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (input_fd == NULL) {
|
||||
uint8_t ULSCH_ids[] = {0};
|
||||
nr_ulsch_pre_encoding(UE, ulsch_ue, 0, 0, &G, 1, ULSCH_ids);
|
||||
nr_ulsch_encoding(UE, ulsch_ue, 0, 0, &G, 1, ULSCH_ids, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -160,6 +160,108 @@ openair0_config_t openair0_cfg[MAX_CARDS];
|
||||
|
||||
channel_desc_t *UE2gNB[MAX_MOBILES_PER_GNB][NUMBER_OF_gNB_MAX];
|
||||
|
||||
static void copy_bytes_to_packed_bits(const uint8_t *in, const uint32_t num_bits, const bool is_ulsch, uint8_t *out)
|
||||
{
|
||||
if (is_ulsch) { // MATLAB computes CRC for input of MSB first
|
||||
for (uint_fast32_t b = 0; b < num_bits; b++) {
|
||||
out[b / 8] |= ((in[b] & 1) << (7 - (b % 8)));
|
||||
}
|
||||
} else {
|
||||
for (uint_fast32_t b = 0; b < num_bits; b++) {
|
||||
out[b / 8] |= (in[b] << (b % 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void prepare_ue_pusch_pdu_from_matlab_vector(const bool uci_on_pusch,
|
||||
FILE *vect_file,
|
||||
nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu,
|
||||
uint8_t *cw_buf)
|
||||
{
|
||||
if (!uci_on_pusch)
|
||||
return;
|
||||
|
||||
if (vect_file == NULL)
|
||||
return;
|
||||
|
||||
struct vect_vars {
|
||||
uint32_t A;
|
||||
uint32_t oack;
|
||||
uint32_t ocsi1;
|
||||
uint32_t ocsi2;
|
||||
uint32_t cwlen;
|
||||
uint32_t cwlen_scr;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct vect_vars var = {0};
|
||||
if (1 != fread(&var, sizeof(var), 1, vect_file)) {
|
||||
printf("Error reading from matlab vector file\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
const uint16_t buff_len = var.A + var.oack + var.ocsi1 + var.ocsi2;
|
||||
uint8_t vec_bits[buff_len];
|
||||
memset(vec_bits, 0, sizeof(vect_file));
|
||||
|
||||
uint8_t *p_vec_bits = vec_bits;
|
||||
if (var.A != fread(p_vec_bits, sizeof(uint8_t), var.A, vect_file)) {
|
||||
printf("Error reading ULSCH bits from file\n");
|
||||
exit(-1);
|
||||
}
|
||||
p_vec_bits += var.A;
|
||||
if (var.oack != fread(p_vec_bits, sizeof(uint8_t), var.oack, vect_file)) {
|
||||
printf("Error reading ACK bits from file\n");
|
||||
exit(-1);
|
||||
}
|
||||
p_vec_bits += var.oack;
|
||||
if (var.ocsi1 != fread(p_vec_bits, sizeof(uint8_t), var.ocsi1, vect_file)) {
|
||||
printf("Error reading CSI1 bits from file\n");
|
||||
exit(-1);
|
||||
}
|
||||
p_vec_bits += var.ocsi1;
|
||||
if (var.ocsi2 != fread(p_vec_bits, sizeof(uint8_t), var.ocsi2, vect_file)) {
|
||||
printf("Error reading CSI2 bits from file\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (var.cwlen != fread(cw_buf, sizeof(uint8_t), var.cwlen, vect_file)) {
|
||||
printf("Error reading cw bits from file\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
memset(cw_buf, 0, var.cwlen_scr);
|
||||
if (var.cwlen_scr != fread(cw_buf, sizeof(uint8_t), var.cwlen_scr, vect_file)) {
|
||||
printf("Error reading cw bits from file\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
uint16_t tb_buf_size = (var.A + 7) / 8;
|
||||
pusch_config_pdu->pusch_data.tb_size = tb_buf_size;
|
||||
pusch_config_pdu->tx_request_body.pdu_length = tb_buf_size;
|
||||
uint8_t *pb = pusch_config_pdu->tx_request_body.fapiTxPdu;
|
||||
memset(pb, 0, tb_buf_size);
|
||||
p_vec_bits = vec_bits;
|
||||
copy_bytes_to_packed_bits(p_vec_bits, var.A, true, pb);
|
||||
|
||||
pusch_config_pdu->pusch_uci.harq_ack_bit_length = var.oack;
|
||||
pb = (uint8_t *)&pusch_config_pdu->pusch_uci.harq_payload;
|
||||
memset(pb, 0, sizeof(pusch_config_pdu->pusch_uci.harq_payload));
|
||||
p_vec_bits += var.A;
|
||||
copy_bytes_to_packed_bits(p_vec_bits, var.oack, false, pb);
|
||||
|
||||
pusch_config_pdu->pusch_uci.csi_part1_bit_length = var.ocsi1;
|
||||
pb = (uint8_t *)&pusch_config_pdu->pusch_uci.csi_part1_payload;
|
||||
memset(pb, 0, sizeof(pusch_config_pdu->pusch_uci.csi_part1_payload));
|
||||
p_vec_bits += var.oack;
|
||||
copy_bytes_to_packed_bits(p_vec_bits, var.ocsi1, false, pb);
|
||||
|
||||
pusch_config_pdu->pusch_uci.csi_part2_bit_length = var.ocsi2;
|
||||
pb = (uint8_t *)&pusch_config_pdu->pusch_uci.csi_part2_payload;
|
||||
memset(pb, 0, sizeof(pusch_config_pdu->pusch_uci.csi_part2_payload));
|
||||
p_vec_bits += var.ocsi1;
|
||||
copy_bytes_to_packed_bits(p_vec_bits, var.ocsi2, false, pb);
|
||||
}
|
||||
|
||||
configmodule_interface_t *uniqCfg = NULL;
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@@ -204,6 +306,8 @@ int main(int argc, char *argv[])
|
||||
int print_perf = 0;
|
||||
cpuf = get_cpu_freq_GHz();
|
||||
int msg3_flag = 0;
|
||||
bool uci_on_pusch = false;
|
||||
bool no_phase_pre_comp = false;
|
||||
int rv_index = 0;
|
||||
float roundStats;
|
||||
double effRate;
|
||||
@@ -227,6 +331,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
UE_nr_rxtx_proc_t UE_proc;
|
||||
FILE *scg_fd=NULL;
|
||||
FILE *uci_ulsch_matlab_vec = NULL;
|
||||
int file_offset = 0;
|
||||
|
||||
double DS_TDL = .03;
|
||||
@@ -250,7 +355,8 @@ int main(int argc, char *argv[])
|
||||
int c;
|
||||
bool setAffinity=false;
|
||||
char gNBthreads[128]="n";
|
||||
while ((c = getopt(argc, argv, "--:O:a:b:c:d:ef:g:h:i:jk:m:n:p:q:r:s:t:u:v:w:y:z:A:C:F:G:H:I:M:N:PR:S:T:U:L:ZW:E:X:Y:")) != -1) {
|
||||
while ((c = getopt(argc, argv, "--:O:a:b:c:d:ef:g:h:i:jk:m:n:o::p:q:r:s:t:u:v:w:y:z:A:C:F:G:H:I:M:N:PR:S:T:U:L:ZW:E:X:Y:"))
|
||||
!= -1) {
|
||||
/* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */
|
||||
/* with this opstring getopt returns 1 for non-option arguments, refer to 'man 3 getopt' */
|
||||
if (c == 1 || c == '-' || c == 'O')
|
||||
@@ -358,6 +464,20 @@ int main(int argc, char *argv[])
|
||||
Imcs = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
uci_on_pusch = true;
|
||||
// UCI on PUSCH is not implemented in OAI gNB yet.
|
||||
// So this flag is needed to verify in MATLAB
|
||||
no_phase_pre_comp = true;
|
||||
if (optarg) { // -o with file input: use matlab vector
|
||||
uci_ulsch_matlab_vec = fopen(optarg, "rb");
|
||||
if (uci_ulsch_matlab_vec == NULL) {
|
||||
printf("Error opening %s\n", optarg);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'W':
|
||||
precod_nbr_layers = atoi(optarg);
|
||||
break;
|
||||
@@ -537,6 +657,7 @@ int main(int argc, char *argv[])
|
||||
printf("-k 3/4 sampling\n");
|
||||
printf("-m MCS value\n");
|
||||
printf("-n Number of trials to simulate\n");
|
||||
printf("-o Enable UCI on PUSCH. Optionally accepts input file (without space). This feature is not yet available in gNB so only used to verify with MATLAB generated vector\n");
|
||||
printf("-p Use extended prefix mode\n");
|
||||
printf("-q MCS table\n");
|
||||
printf("-r Number of allocated resource blocks for PUSCH\n");
|
||||
@@ -788,7 +909,8 @@ int main(int argc, char *argv[])
|
||||
UE->if_inst->phy_config_request = nr_ue_phy_config_request;
|
||||
UE->if_inst->dl_indication = nr_ue_dl_indication;
|
||||
UE->if_inst->ul_indication = nr_ue_ul_indication;
|
||||
|
||||
UE->no_phase_pre_comp = no_phase_pre_comp;
|
||||
|
||||
UE_mac->if_module = nr_ue_if_module_init(0);
|
||||
|
||||
initFloatingCoresTpool(threadCnt, &nrUE_params.Tpool, false, "UE-tpool");
|
||||
@@ -926,6 +1048,9 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
unsigned int available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, number_dmrs_symbols, unav_res, mod_order, precod_nbr_layers);
|
||||
uint8_t cw_buf[available_bits];
|
||||
memset(cw_buf, 0, available_bits);
|
||||
UE->phy_sim_test_buf = calloc(1, (available_bits + 7) / 8);
|
||||
printf("[ULSIM]: VALUE OF G: %u, TBS: %u\n", available_bits, TBS);
|
||||
|
||||
int frame_length_complex_samples = gNB->frame_parms.samples_per_subframe * NR_NUMBER_OF_SUBFRAMES_PER_FRAME;
|
||||
@@ -1221,6 +1346,20 @@ int main(int argc, char *argv[])
|
||||
// pusch_config_pdu->pdu_bit_map |= PUSCH_PDU_BITMAP_DFTS_OFDM;
|
||||
pusch_config_pdu->num_dmrs_cdm_grps_no_data = num_dmrs_cdm_grps_no_data;
|
||||
}
|
||||
if (uci_on_pusch) {
|
||||
const nfapi_nr_ue_pusch_uci_t pusch_uci = {
|
||||
.alpha_scaling = 3,
|
||||
.beta_offset_csi1 = 13,
|
||||
.beta_offset_csi2 = 13,
|
||||
.beta_offset_harq_ack = 11,
|
||||
.harq_ack_bit_length = 3,
|
||||
.harq_payload = 3,
|
||||
//.csi_part1_bit_length = 4,
|
||||
//.csi_part1_payload = 15
|
||||
};
|
||||
pusch_config_pdu->pusch_uci = pusch_uci;
|
||||
prepare_ue_pusch_pdu_from_matlab_vector(uci_on_pusch, uci_ulsch_matlab_vec, pusch_config_pdu, cw_buf);
|
||||
}
|
||||
|
||||
if (do_SRS == 1) {
|
||||
fapi_nr_ul_config_request_pdu_t *ul_config1 = &ul_config.ul_config_list[1];
|
||||
@@ -1456,7 +1595,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if ((ulsch_gNB->last_iteration_cnt >= ulsch_gNB->max_ldpc_iterations) || ul_proc_error == 1) {
|
||||
error_flag = 1;
|
||||
error_flag = uci_on_pusch ? 0 : 1;
|
||||
n_errors[round]++;
|
||||
crc_status = 1;
|
||||
} else
|
||||
@@ -1473,15 +1612,32 @@ int main(int argc, char *argv[])
|
||||
available_bits, (ptrsSymbPerSlot * ptrsRePerSymb * mod_order * precod_nbr_layers));
|
||||
}
|
||||
|
||||
for (i = 0; i < available_bits; i++) {
|
||||
if (((UE->ul_harq_processes[harq_pid].f[i] == 0) && (pusch_vars->llr[i] <= 0))
|
||||
|| ((UE->ul_harq_processes[harq_pid].f[i] == 1) && (pusch_vars->llr[i] >= 0))) {
|
||||
/*if(errors_scrambling == 0)
|
||||
printf("\x1B[34m" "[frame %d][trial %d]\t1st bit in error in unscrambling = %d\n" "\x1B[0m", frame, trial, i);*/
|
||||
errors_scrambling[round]++;
|
||||
if (uci_on_pusch) {
|
||||
for (i = 0; i < available_bits; i++) {
|
||||
uint8_t ue_byte = UE->phy_sim_test_buf[i / 8];
|
||||
uint8_t ue_bit = (ue_byte >> (i % 8)) & 1;
|
||||
uint8_t test_vector_bit = cw_buf[i] & 1;
|
||||
if (ue_bit != test_vector_bit)
|
||||
errors_scrambling[round]++;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < available_bits; i++) {
|
||||
uint8_t ue_byte = UE->ul_harq_processes[harq_pid].f[i / 8];
|
||||
uint8_t ue_bit = ue_byte >> (i % 8) & 1;
|
||||
uint8_t gnb_llr = pusch_vars->llr[i];
|
||||
if (((ue_bit == 0) && (gnb_llr <= 0)) || ((ue_bit == 1) && (gnb_llr >= 0))) {
|
||||
errors_scrambling[round]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
round++;
|
||||
if (uci_on_pusch && uci_ulsch_matlab_vec && (errors_scrambling[round] == 0)) {
|
||||
ret = 0;
|
||||
printf("*************\n");
|
||||
printf("UCI on PUSCH test OK against MATLAB generated codeword\n");
|
||||
printf("*************\n");
|
||||
break;
|
||||
}
|
||||
} // round
|
||||
|
||||
if (n_trials == 1 && errors_scrambling[0] > 0) {
|
||||
@@ -1651,5 +1807,10 @@ int main(int argc, char *argv[])
|
||||
free(filename_csv);
|
||||
}
|
||||
|
||||
if (uci_ulsch_matlab_vec)
|
||||
fclose(uci_ulsch_matlab_vec);
|
||||
|
||||
free_and_zero(UE->phy_sim_test_buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
BIN
openair1/SIMULATION/TOOLS/uci_on_pusch_1.bin
Normal file
BIN
openair1/SIMULATION/TOOLS/uci_on_pusch_1.bin
Normal file
Binary file not shown.
BIN
openair1/SIMULATION/TOOLS/uci_on_pusch_2.bin
Normal file
BIN
openair1/SIMULATION/TOOLS/uci_on_pusch_2.bin
Normal file
Binary file not shown.
BIN
openair1/SIMULATION/TOOLS/uci_on_pusch_3.bin
Normal file
BIN
openair1/SIMULATION/TOOLS/uci_on_pusch_3.bin
Normal file
Binary file not shown.
79
openair1/SIMULATION/TOOLS/uci_on_pusch_decode.m
Normal file
79
openair1/SIMULATION/TOOLS/uci_on_pusch_decode.m
Normal file
@@ -0,0 +1,79 @@
|
||||
% ULSIM command line
|
||||
% ./nr_ulsim -m 27 -u 1 -R 51 -r 51 -s 300 -L 4 -o
|
||||
|
||||
carrier = nrCarrierConfig;
|
||||
carrier.NSizeGrid = 51;
|
||||
carrier.NSlot = 8;
|
||||
carrier.SubcarrierSpacing = 30;
|
||||
carrier.CyclicPrefix = "normal";
|
||||
|
||||
pusch = nrPUSCHConfig;
|
||||
pusch.PRBSet = 0:carrier.NSizeGrid-1;
|
||||
pusch.SymbolAllocation = [0,12];
|
||||
pusch.MappingType = "B";
|
||||
pusch.NID = 0;
|
||||
pusch.RNTI = 4660;
|
||||
pusch.NumLayers = 1;
|
||||
pusch.TransformPrecoding = false;
|
||||
pusch.TransmissionScheme = "nonCodebook";
|
||||
pusch.NumAntennaPorts = 1;
|
||||
pusch.TPMI = 0;
|
||||
pusch.Modulation = "64QAM";
|
||||
|
||||
pusch.DMRS.DMRSConfigurationType = 1;
|
||||
pusch.DMRS.DMRSTypeAPosition = 2;
|
||||
pusch.DMRS.NumCDMGroupsWithoutData = 1;
|
||||
pusch.DMRS.DMRSAdditionalPosition = 0;
|
||||
pusch.DMRS.NIDNSCID = 0;
|
||||
pusch.DMRS.NRSID = 0;
|
||||
pusch.DMRS.NSCID = 0;
|
||||
pusch.DMRS.GroupHopping = 0;
|
||||
pusch.DMRS.SequenceHopping = 0;
|
||||
|
||||
pusch.BetaOffsetACK = 20;
|
||||
pusch.BetaOffsetCSI1 = 6.25;
|
||||
pusch.BetaOffsetCSI2 = 1;
|
||||
pusch.UCIScaling = 1;
|
||||
|
||||
[puschIndices,puschIndicesInfo] = nrPUSCHIndices(carrier,pusch);
|
||||
|
||||
dmrsLayerSymbols = nrPUSCHDMRS(carrier,pusch);
|
||||
dmrsLayerIndices = nrPUSCHDMRSIndices(carrier,pusch);
|
||||
|
||||
run("../../../cmake_targets/ran_build/build/txsig0.m");
|
||||
rxGrid = nrOFDMDemodulate(carrier,txs0);
|
||||
[estChannelGrid,noiseEst] = nrChannelEstimate(carrier,rxGrid,dmrsLayerIndices,dmrsLayerSymbols);
|
||||
[puschRx,puschHest] = nrExtractResources(puschIndices,rxGrid,estChannelGrid);
|
||||
[puschEq,csi] = nrEqualizeMMSE(puschRx,puschHest,noiseEst);
|
||||
|
||||
decodeULSCH = nrULSCHDecoder;
|
||||
decodeULSCH.MultipleHARQProcesses = false;
|
||||
decodeULSCH.CBGTransmission = false;
|
||||
decodeULSCH.TargetCodeRate = 910 / 1024;
|
||||
decodeULSCH.LDPCDecodingAlgorithm = 'Normalized min-sum';
|
||||
decodeULSCH.MaximumLDPCIterationCount = 6;
|
||||
decodeULSCH.TransportBlockLength = 37896;
|
||||
|
||||
oack = 3;
|
||||
ocsi1 = 0;
|
||||
ocsi2 = 0;
|
||||
[ulschLLRs,rxSymbols] = nrPUSCHDecode(carrier,pusch,decodeULSCH.TargetCodeRate,...
|
||||
decodeULSCH.TransportBlockLength,oack,ocsi1,ocsi2,puschEq,noiseEst);
|
||||
rmInfo = nrULSCHInfo(pusch,decodeULSCH.TargetCodeRate,...
|
||||
decodeULSCH.TransportBlockLength,oack,ocsi1,ocsi2);
|
||||
[culsch,cack,ccsi1,ccsi2] = nrULSCHDemultiplex(pusch,decodeULSCH.TargetCodeRate,...
|
||||
decodeULSCH.TransportBlockLength,oack,ocsi1,ocsi2,ulschLLRs);
|
||||
|
||||
plot(rxSymbols,'*');
|
||||
|
||||
decodeULSCH.reset();
|
||||
rv = 0; % Redundancy version for decoding
|
||||
[decodedBits,blkerr] = decodeULSCH(culsch,pusch.Modulation,pusch.NumLayers,rv);
|
||||
% Display the decoded bits
|
||||
disp('Block error:');
|
||||
disp(blkerr);
|
||||
|
||||
ucibits = nrUCIDecode(cack,oack);
|
||||
% Display the decoded UCI bits
|
||||
disp('Decoded UCI bits:');
|
||||
disp(ucibits);
|
||||
85
openair1/SIMULATION/TOOLS/uci_on_pusch_encode.m
Normal file
85
openair1/SIMULATION/TOOLS/uci_on_pusch_encode.m
Normal file
@@ -0,0 +1,85 @@
|
||||
% ulsim command for same pusch config
|
||||
% ./nr_ulsim -m 27 -u 1 -R 51 -r 51 -s 300 -o uci_on_pusch_7.bin
|
||||
|
||||
carrier = nrCarrierConfig;
|
||||
carrier.NSizeGrid = 51;
|
||||
carrier.NSlot = 8;
|
||||
carrier.SubcarrierSpacing = 30;
|
||||
carrier.CyclicPrefix = "normal";
|
||||
|
||||
pusch = nrPUSCHConfig;
|
||||
pusch.PRBSet = 0:carrier.NSizeGrid-1;
|
||||
pusch.SymbolAllocation = [0,12];
|
||||
pusch.MappingType = "B";
|
||||
pusch.NID = 0;
|
||||
pusch.RNTI = 4660;
|
||||
pusch.NumLayers = 1;
|
||||
pusch.TransformPrecoding = false;
|
||||
pusch.TransmissionScheme = "nonCodebook";
|
||||
pusch.NumAntennaPorts = 1;
|
||||
pusch.TPMI = 0;
|
||||
pusch.Modulation = "64QAM";
|
||||
|
||||
pusch.DMRS.DMRSConfigurationType = 1;
|
||||
pusch.DMRS.DMRSTypeAPosition = 2;
|
||||
pusch.DMRS.NumCDMGroupsWithoutData = 1;
|
||||
pusch.DMRS.DMRSAdditionalPosition = 0;
|
||||
pusch.DMRS.NIDNSCID = 0;
|
||||
pusch.DMRS.NRSID = 0;
|
||||
pusch.DMRS.NSCID = 0;
|
||||
pusch.DMRS.GroupHopping = 0;
|
||||
pusch.DMRS.SequenceHopping = 0;
|
||||
|
||||
pusch.BetaOffsetACK = 20;
|
||||
pusch.BetaOffsetCSI1 = 6.25;
|
||||
pusch.BetaOffsetCSI2 = 1;
|
||||
pusch.UCIScaling = 1;
|
||||
|
||||
A = 37896;
|
||||
rate = 910 / 1024;
|
||||
rv = 0;
|
||||
modulation = pusch.Modulation;
|
||||
nlayers = pusch.NumLayers; % Number of layers for decoding
|
||||
|
||||
oack = 7;
|
||||
ocsi1 = 0;
|
||||
ocsi2 = 0;
|
||||
cbsInfo = nrULSCHInfo(pusch, rate, A, oack, ocsi1, ocsi2); % Get ULSCH information
|
||||
|
||||
ack = randi([0 1],oack,1);
|
||||
csi1 = randi([0 1],ocsi1,1);
|
||||
csi2 = randi([0 1],ocsi2,1);
|
||||
cack = nrUCIEncode(ack,cbsInfo.GACK,pusch.Modulation);
|
||||
ccsi1 = [];
|
||||
ccsi2 = [];
|
||||
|
||||
tb_data = randi([0 1],A,1);
|
||||
% Transport block CRC attachment
|
||||
tbIn = nrCRCEncode(tb_data,cbsInfo.CRC);
|
||||
|
||||
% Code block segmentation and CRC attachment
|
||||
cbsIn = nrCodeBlockSegmentLDPC(tbIn,cbsInfo.BGN);
|
||||
|
||||
% LDPC encoding
|
||||
enc = nrLDPCEncode(cbsIn,cbsInfo.BGN);
|
||||
|
||||
% Rate matching and code block concatenation
|
||||
chIn = nrRateMatchLDPC(enc,cbsInfo.GULSCH,rv,modulation,nlayers);
|
||||
culsch = chIn;
|
||||
|
||||
|
||||
% Get the codeword and locations of each type (data and UCI)
|
||||
[cw,indInfo] = nrULSCHMultiplex(pusch,rate,A,culsch,cack,ccsi1,ccsi2);
|
||||
cw_scr = nrPUSCHScramble(cw,pusch.NID,pusch.RNTI);
|
||||
|
||||
% Write to bin file
|
||||
fid = fopen('uci_on_pusch_7.bin', 'wb');
|
||||
to_write = [A,oack,ocsi1,ocsi2,length(cw),length(cw_scr)];
|
||||
fwrite(fid, to_write, 'uint32'); % Write the data to the binary file
|
||||
fwrite(fid, tb_data, "uint8");
|
||||
fwrite(fid, ack, "uint8");
|
||||
fwrite(fid, csi1, "uint8");
|
||||
fwrite(fid, csi2, "uint8");
|
||||
fwrite(fid, cw, "uint8");
|
||||
fwrite(fid, cw_scr, "uint8");
|
||||
fclose(fid); % Close the file after writing
|
||||
Reference in New Issue
Block a user