Compare commits

...

13 Commits

Author SHA1 Message Date
Sakthivel Velumani
d3cfbff22f ulsim: add UCI on PUSCH test in CI
matlab generated codeword file is placed in SIMULATION/TOOLS. This is
used by ulsim to compare if codeword generated matches.
2025-08-27 12:36:34 -04:00
Sakthivel Velumani
03f55a1616 ulsim: test UCI on PUSCH with matlab
Since there is no UCI on PUSCH implementation in gNB, it is good to test
UE's implementation with matlab.

This commit allows to test with matlab in two ways:
1. generate OFDM waveform from ulsim and decode it in matlab using
uci_on_pusch_decode.m
2. generate codeword in matlab using uci_on_pusch_encode.m read it in
ulsim and compare the bits. ulsim option -o is used
2025-08-27 12:35:08 -04:00
Sakthivel Velumani
a47a3d7aea Add flag for Tx phase pre compensation
Used in ulsim to verify waveform in MATLAB
2025-08-27 11:16:20 -04:00
Sakthivel Velumani
215f881ebe ulsim: Fix error bits detection before decoding 2025-08-27 11:16:14 -04:00
Sakthivel Velumani
8215e06c3d Add processing stats for UCI mapping on PUSCH 2025-08-17 09:24:59 -04:00
Sakthivel Velumani
9fa8564858 Simplify code 2025-08-16 01:25:26 -04:00
alexjiao
8145f91ad7 Add functions to calculate M_bit for PUCCH format 2/3/4
Add functions to calculate PUCCH format 2/3/4 rate matching output sequence length (M_bit),
according to 3GPP TS 38.212 Table 6.3.1.4-1.
2025-08-16 01:25:26 -04:00
alexjiao
3b4bbe8891 Handle UCI on PUSCH scrambling
When the codeword has UCI multiplexed with data, we need to handle UCI scrambling according to 38.211 section 6.3.1.1.
Especially when O_ACK <= 2 there are x and y placeholder bits for UCI.
2025-08-16 01:25:26 -04:00
alexjiao
904b8df586 Handle UCI on PUSCH procedures at PHY
Get beta offset, alpha scaling and other parameters to calculate rate matching information for UCI on PUSCH,
then use the rate matching info to encode UCI and re-encode PUSCH, call nr_uci_on_pusch to get final multiplexed codeword for scrambling.
2025-08-16 01:25:26 -04:00
Sakthivel Velumani
1dd43cd6db Separate segmentation from ULSCH encoding function
Segmentation have to be called before UCI mapping to get number of
segments and sumKr values.
2025-08-16 01:25:21 -04:00
alexjiao
a98a6eae30 Add functions to implement UCI and data multiplexing on PUSCH
Data and control multiplexing procedure is defined in 38.212 section 6.2.7.
Step 3 (CSI1 and CSI2 mapping) is not implemented, only HARQ-ACK.
PUSCH frequency hopping is not supported.
2025-08-15 22:58:26 -04:00
alexjiao
f496294186 Add functions to calculate rate matching information for UCI on PUSCH
Use betaOffset and alpha scaling etc. to get rate matching information of UCI on PUSCH (e.g. max bit capacity of encoded UCI and encoded ULSCH)
according to TS 38.212 section 6.3.2.4 and 6.2.7.
2025-08-15 22:58:26 -04:00
alexjiao
db092ffd6c Modify nr_uci_encoding for UCI on PUSCH
38.212 section 5.3.3.1 and 5.3.3.2 describe how to encode 1-bit and 2-bit information.
This is needed when typically HARQ-ACK will be transmitted on PUSCH.
38.212 section 5.4.3 also describes how to do rate matching for channel coding of small block lengths.
2025-08-15 22:58:26 -04:00
22 changed files with 1382 additions and 222 deletions

View File

@@ -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/

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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++) {

View File

@@ -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)

View File

@@ -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};

View File

@@ -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];

View File

@@ -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),\

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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);

View 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