Commit 75158a58 authored by Raymond Knopp's avatar Raymond Knopp

nr_dlsim compiles

parent 930e1fde
......@@ -2638,7 +2638,7 @@ target_link_libraries (dlsim_tm4
add_executable(polartest
${OPENAIR1_DIR}/PHY/CODING/TESTBENCH/polartest.c
${OPENAIR_DIR}/common/utils/backtrace.c
${OPENAIR_DIR}/common/utils/nr/nr_common.c)
${OPENAIR_DIR}/common/utils/nr/nr_common.c
${OPENAIR_DIR}/common/utils/system.c
${T_SOURCE}
${SHLIB_LOADER_SOURCES}
......
......@@ -50,13 +50,16 @@ int NRRIV2PRBOFFSET(int locationAndBandwidth,int N_RB) {
else return(N_RB-tmp2);
}
int PRBalloc_to_locationandbandwidth(int NPRB,int RBstart) {
int PRBalloc_to_locationandbandwidth0(int NPRB,int RBstart,int BWPsize) {
if (NPRB < 138)
return(275*(NPRB-1)+RBstart);
return(BWPsize*(NPRB-1)+RBstart);
else
return(275*(276-NPRB) + (274-RBstart));
return(BWPsize*(BWPsize+1-NPRB) + (BWPsize-1-RBstart));
}
int PRBalloc_to_locationandbandwidth(int NPRB,int RBstart) {
return(PRBalloc_to_locationandbandwidth0(NPRB,RBstart,275));
}
/// Target code rate tables indexed by Imcs
uint16_t nr_target_code_rate_table1[29] = {120, 157, 193, 251, 308, 379, 449, 526, 602, 679, 340, 378, 434, 490, 553, \
616, 658, 438, 466, 517, 567, 616, 666, 719, 772, 822, 873, 910, 948};
......@@ -128,3 +131,17 @@ int get_subband_size(int NPRB,int size) {
AssertFatal(1==0,"Shouldn't get here, NPRB %d\n",NPRB);
}
void SLIV2SL(int SLIV,int *S,int *L) {
int SLIVdiv14 = SLIV/14;
int SLIVmod14 = SLIV%14;
// Either SLIV = 14*(L-1) + S, or SLIV = 14*(14-L+1) + (14-1-S). Condition is 0 <= L <= 14-S
if ((SLIVdiv14 + 1) >= 0 && (SLIVdiv14 <= 13-SLIVmod14)) {
*L=SLIVdiv14+1;
*S=SLIVmod14;
} else {
*L=15-SLIVdiv14;
*S=13-SLIVmod14;
}
}
......@@ -35,6 +35,7 @@
int NRRIV2BW(int locationAndBandwidth,int N_RB);
int NRRIV2PRBOFFSET(int locationAndBandwidth,int N_RB);
int PRBalloc_to_locationandbandwidth0(int NPRB,int RBstart,int BWPsize);
int PRBalloc_to_locationandbandwidth(int NPRB,int RBstart);
extern uint16_t nr_target_code_rate_table1[29];
extern uint16_t nr_target_code_rate_table2[28];
......@@ -43,3 +44,4 @@ extern uint16_t nr_tbs_table[93];
uint8_t nr_get_Qm(uint8_t Imcs, uint8_t table_idx);
uint32_t nr_get_code_rate(uint8_t Imcs, uint8_t table_idx);
int get_subband_size(int NPRB,int size);
void SLIV2SL(int SLIV,int *S,int *L);
......@@ -604,51 +604,96 @@ typedef struct {
nfapi_nr_dl_config_bch_pdu_rel15_t bch_pdu_rel15;
} nfapi_nr_dl_config_bch_pdu;
typedef struct {
/// Number of PRGs spanning this allocation. Value : 1->275
uint16_t numPRGs;
/// Size in RBs of a precoding resource block group (PRG) – to which same precoding and digital beamforming gets applied. Value: 1->275
uint16_t prgSize;
/// Number of STD ant ports (parallel streams) feeding into the digBF Value: 0->255
uint8_t digBFInterfaces;
uint16_t PMIdx[275];
uint16_t *beamIdx[275];
} nr_beamforming_t;
typedef struct {
nfapi_tl_t tl;
uint16_t length;
uint8_t pdu_index;
uint16_t pduBitmap;
uint16_t rnti;
uint16_t nb_mod_symbols;
uint8_t time_allocation_type;
uint8_t freq_allocation_type;
uint16_t start_prb;
uint16_t n_prb;
uint8_t start_symbol;
uint8_t nb_symbols;
uint8_t mcs_idx;
uint8_t mcs_table;
uint8_t ndi;
uint8_t nb_codewords;
uint8_t nb_layers;
uint16_t coding_rate;
uint8_t modulation;
uint8_t modulation_order;
uint32_t transport_block_size;
uint8_t dmrs_Type;
uint8_t dmrs_TypeA_Position;
uint8_t dmrs_maxLength;
uint8_t dmrs_AdditionalPosition;
uint8_t time_alloc_list_flag;
uint8_t time_alloc_list;
uint8_t mapping_type;
uint8_t rbg_list;
uint8_t virtual_resource_block_assignment_flag;
uint32_t resource_block_coding;
uint8_t redundancy_version;
uint8_t transport_blocks;
uint8_t transmission_scheme;
uint8_t number_of_subbands;
uint8_t codebook_index[NFAPI_MAX_NUM_SUBBANDS];
uint8_t ue_category_capacity;
uint8_t x_overhead;
uint8_t pa;
uint8_t delta_power_offset_index;
uint8_t ngap;
uint8_t transmission_mode;
uint8_t num_bf_prb_per_subband;
uint8_t num_bf_vector;
nfapi_bf_vector_t bf_vector[NFAPI_MAX_BF_VECTORS];
uint16_t pduIndex;
// BWP [TS38.213 sec 12]
/// Bandwidth part size [TS38.213 sec12]. Number of contiguous PRBs allocated to the BWP, Value: 1->275
uint16_t BWPSize;
/// bandwidth part start RB index from reference CRB [TS38.213 sec 12],Value: 0->274
uint16_t BWPStart;
/// subcarrierSpacing [TS38.211 sec 4.2], Value:0->4
uint8_t SubcarrierSpacing;
/// Cyclic prefix type [TS38.211 sec 4.2], 0: Normal; 1: Extended
uint8_t CyclicPrefix;
// Codeword information
/// Number of code words for this RNTI (UE), Value: 1 -> 2
uint8_t NrOfCodewords;
/// Target coding rate [TS38.212 sec 5.4.2.1 and 38.214 sec 5.1.3.1]. This is the number of information bits per 1024 coded bits expressed in 0.1 bit units
uint16_t targetCodeRate[2];
/// QAM modulation [TS38.212 sec 5.4.2.1 and 38.214 sec 5.1.3.1], Value: 2,4,6,8
uint8_t qamModOrder[2];
/// MCS index [TS38.214, sec 5.1.3.1], should match value sent in DCI Value : 0->31
uint8_t mcsIndex[2];
/// MCS-Table-PDSCH [TS38.214, sec 5.1.3.1] 0: notqam256, 1: qam256, 2: qam64LowSE
uint8_t mcsTable[2];
/// Redundancy version index [TS38.212, Table 5.4.2.1-2 and 38.214, Table 5.1.2.1-2], should match value sent in DCI Value : 0->3
uint8_t rvIndex[2];
/// Transmit block size (in bytes) [TS38.214 sec 5.1.3.2], Value: 0->65535
uint32_t TBSize[2];
/// dataScramblingIdentityPdsch [TS38.211, sec 7.3.1.1], It equals the higher-layer parameter Datascrambling-Identity if configured and the RNTI equals the C-RNTI, otherwise L2 needs to set it to physical cell id. Value: 0->65535
uint16_t dataScramblingId;
/// Number of layers [TS38.211, sec 7.3.1.3]. Value : 1->8
uint8_t nrOfLayers;
/// PDSCH transmission schemes [TS38.214, sec5.1.1] 0: Up to 8 transmission layers
uint8_t transmissionScheme;
/// Reference point for PDSCH DMRS "k" - used for tone mapping [TS38.211, sec 7.4.1.1.2] Resource block bundles [TS38.211, sec 7.3.1.6] Value: 0 -> 1 If 0, the 0 reference point for PDSCH DMRS is at Point A [TS38.211 sec 4.4.4.2]. Resource block bundles generated per sub-bullets 2 and 3 in [TS38.211, sec 7.3.1.6]. For sub-bullet 2, the start of bandwidth part must be set to the start of actual bandwidth part +NstartCORESET and the bandwidth of the bandwidth part must be set to the bandwidth of the initial bandwidth part. If 1, the DMRS reference point is at the lowest VRB/PRB of the allocation. Resource block bundles generated per sub-bullets 1 [TS38.211, sec 7.3.1.6]
uint8_t refPoint;
// DMRS [TS38.211 sec 7.4.1.1]
/// DMRS symbol positions [TS38.211, sec 7.4.1.1.2 and Tables 7.4.1.1.2-3 and 7.4.1.1.2-4] Bitmap occupying the 14 LSBs with: bit 0: first symbol and for each bit 0: no DMRS 1: DMRS
uint16_t dlDmrsSymbPos;
/// DL DMRS config type [TS38.211, sec 7.4.1.1.2] 0: type 1, 1: type 2
uint8_t dmrsConfigType;
/// DL-DMRS-Scrambling-ID [TS38.211, sec 7.4.1.1.2 ] If provided by the higher-layer and the PDSCH is scheduled by PDCCH with CRC scrambled by CRNTI or CS-RNTI, otherwise, L2 should set this to physical cell id. Value: 0->65535
uint16_t dlDmrsScramblingId;
/// DMRS sequence initialization [TS38.211, sec 7.4.1.1.2]. Should match what is sent in DCI 1_1, otherwise set to 0. Value : 0->1
uint8_t SCID;
/// Number of DM-RS CDM groups without data [TS38.212 sec 7.3.1.2.2] [TS38.214 Table 4.1-1] it determines the ratio of PDSCH EPRE to DM-RS EPRE. Value: 1->3
uint8_t numDmrsCdmGrpsNoData;
/// DMRS ports. [TS38.212 7.3.1.2.2] provides description between DCI 1-1 content and DMRS ports. Bitmap occupying the 11 LSBs with: bit 0: antenna port 1000 bit 11: antenna port 1011 and for each bit 0: DMRS port not used 1: DMRS port used
uint16_t dmrsPorts;
// Pdsch Allocation in frequency domain [TS38.214, sec 5.1.2.2]
/// Resource Allocation Type [TS38.214, sec 5.1.2.2] 0: Type 0, 1: Type 1
uint8_t resourceAlloc;
/// For resource alloc type 0. TS 38.212 V15.0.x, 7.3.1.2.2 bitmap of RBs, 273 rounded up to multiple of 32. This bitmap is in units of VRBs. LSB of byte 0 of the bitmap represents the first RB of the bwp
uint8_t rbBitmap[36];
/// For resource allocation type 1. [TS38.214, sec 5.1.2.2.2] The starting resource block within the BWP for this PDSCH. Value: 0->274
uint16_t rbStart;
/// For resource allocation type 1. [TS38.214, sec 5.1.2.2.2] The number of resource block within for this PDSCH. Value: 1->275
uint16_t rbSize;
/// VRB-to-PRB-mapping [TS38.211, sec 7.3.1.6] 0: non-interleaved 1: interleaved with RB size 2 2: Interleaved with RB size 4
uint8_t VRBtoPRBMapping;
// Resource Allocation in time domain [TS38.214, sec 5.1.2.1]
/// Start symbol index of PDSCH mapping from the start of the slot, S. [TS38.214, Table 5.1.2.1-1] Value: 0->13
uint8_t StartSymbolIndex;
/// PDSCH duration in symbols, L [TS38.214, Table 5.1.2.1-1] Value: 1->14
uint8_t NrOfSymbols;
// PTRS [TS38.214, sec 5.1.6.3]
/// PT-RS antenna ports [TS38.214, sec 5.1.6.3] [TS38.211, table 7.4.1.2.2-1] Bitmap occupying the 6 LSBs with: bit 0: antenna port 1000 bit 5: antenna port 1005 and for each bit 0: PTRS port not used 1: PTRS port used
uint8_t PTRSPortIndex ;
/// PT-RS time density [TS38.214, table 5.1.6.3-1] 0: 1 1: 2 2: 4
uint8_t PTRSTimeDensity;
/// PT-RS frequency density [TS38.214, table 5.1.6.3-2] 0: 2 1: 4
uint8_t PTRSFreqDensity;
/// PT-RS resource element offset [TS38.211, table 7.4.1.2.2-1] Value: 0->3
uint8_t PTRSReOffset;
/// PT-RS-to-PDSCH EPRE ratio [TS38.214, table 4.1-2] Value :0->3
uint8_t nEpreRatioOfPDSCHToPTRS;
// Beamforming
nr_beamforming_t precodingAndBeamforming;
}nfapi_nr_dl_config_dlsch_pdu_rel15_t;
#define NFAPI_NR_DL_CONFIG_REQUEST_DLSCH_PDU_REL15_TAG
......
This diff is collapsed.
......@@ -74,6 +74,7 @@ uint8_t nr_generate_pdsch(NR_gNB_DLSCH_t *dlsch,
int frame,
uint8_t slot,
NR_DL_FRAME_PARMS *frame_parms,
int xOverhead,
nfapi_nr_config_request_t *config,
time_stats_t *dlsch_encoding_stats,
time_stats_t *dlsch_scrambling_stats,
......@@ -82,12 +83,17 @@ uint8_t nr_generate_pdsch(NR_gNB_DLSCH_t *dlsch,
NR_DL_gNB_HARQ_t *harq = dlsch->harq_processes[dci_alloc->harq_pid];
nfapi_nr_dl_config_dlsch_pdu_rel15_t *rel15 = &harq->dlsch_pdu.dlsch_pdu_rel15;
nfapi_nr_dl_config_pdcch_parameters_rel15_t pdcch_params = dci_alloc->pdcch_params;
uint8_t rnti_type = pdcch_params.rnti_type;
uint16_t N_PRB_oh = ((rnti_type==NFAPI_NR_RNTI_SI)||(rnti_type==NFAPI_NR_RNTI_RA)||(rnti_type==NFAPI_NR_RNTI_P))? 0 : \
(xOverhead);
uint8_t N_PRB_DMRS = (rel15->dmrsConfigType == NFAPI_NR_DMRS_TYPE1)?6:4; //This only works for antenna port 1000
uint8_t N_sh_symb = rel15->NrOfSymbols;
uint32_t scrambled_output[NR_MAX_NB_CODEWORDS][NR_MAX_PDSCH_ENCODED_LENGTH>>5];
int16_t **mod_symbs = (int16_t**)dlsch->mod_symbs;
int16_t **tx_layers = (int16_t**)dlsch->txdataF;
int8_t Wf[2], Wt[2], l0, l_prime[2], delta;
uint16_t nb_symbols = rel15->nb_mod_symbols;
uint8_t Qm = rel15->modulation_order;
uint16_t nb_symbols = rel15->NrOfSymbols;
uint8_t Qm = rel15->qamModOrder[0];
uint32_t encoded_length = nb_symbols*Qm;
/// CRC, coding, interleaving and rate matching
......@@ -96,30 +102,30 @@ uint8_t nr_generate_pdsch(NR_gNB_DLSCH_t *dlsch,
nr_dlsch_encoding(harq->pdu, frame, slot, dlsch, frame_parms);
stop_meas(dlsch_encoding_stats);
#ifdef DEBUG_DLSCH
printf("PDSCH encoding:\nPayload:\n");
for (int i=0; i<harq->B>>7; i++) {
for (int j=0; j<16; j++)
printf("0x%02x\t", harq->pdu[(i<<4)+j]);
printf("PDSCH encoding:\nPayload:\n");
for (int i=0; i<harq->B>>7; i++) {
for (int j=0; j<16; j++)
printf("0x%02x\t", harq->pdu[(i<<4)+j]);
printf("\n");
}
printf("\nEncoded payload:\n");
for (int i=0; i<encoded_length>>3; i++) {
for (int j=0; j<8; j++)
printf("%d", harq->f[(i<<3)+j]);
printf("\t");
}
printf("\n");
}
printf("\nEncoded payload:\n");
for (int i=0; i<encoded_length>>3; i++) {
for (int j=0; j<8; j++)
printf("%d", harq->f[(i<<3)+j]);
printf("\t");
}
printf("\n");
#endif
/// scrambling
start_meas(dlsch_scrambling_stats);
for (int q=0; q<rel15->nb_codewords; q++)
for (int q=0; q<rel15->NrOfCodewords; q++)
memset((void*)scrambled_output[q], 0, (encoded_length>>5)*sizeof(uint32_t));
uint16_t n_RNTI = (pdcch_params.search_space_type == NFAPI_NR_SEARCH_SPACE_TYPE_UE_SPECIFIC)? \
((pdcch_params.scrambling_id==0)?pdcch_params.rnti:0) : 0;
uint16_t Nid = (pdcch_params.search_space_type == NFAPI_NR_SEARCH_SPACE_TYPE_UE_SPECIFIC)? \
pdcch_params.scrambling_id : config->sch_config.physical_cell_id.value;
for (int q=0; q<rel15->nb_codewords; q++)
for (int q=0; q<rel15->NrOfCodewords; q++)
nr_pdsch_codeword_scrambling(harq->f,
encoded_length,
q,
......@@ -129,64 +135,64 @@ printf("\n");
stop_meas(dlsch_scrambling_stats);
#ifdef DEBUG_DLSCH
printf("PDSCH scrambling:\n");
for (int i=0; i<encoded_length>>8; i++) {
for (int j=0; j<8; j++)
printf("0x%08x\t", scrambled_output[0][(i<<3)+j]);
printf("\n");
}
printf("PDSCH scrambling:\n");
for (int i=0; i<encoded_length>>8; i++) {
for (int j=0; j<8; j++)
printf("0x%08x\t", scrambled_output[0][(i<<3)+j]);
printf("\n");
}
#endif
/// Modulation
start_meas(dlsch_modulation_stats);
for (int q=0; q<rel15->nb_codewords; q++)
for (int q=0; q<rel15->NrOfCodewords; q++)
nr_modulation(scrambled_output[q],
encoded_length,
Qm,
mod_symbs[q]);
stop_meas(dlsch_modulation_stats);
#ifdef DEBUG_DLSCH
printf("PDSCH Modulation: Qm %d(%d)\n", Qm, nb_symbols);
for (int i=0; i<nb_symbols>>3; i++) {
for (int j=0; j<8; j++) {
printf("%d %d\t", mod_symbs[0][((i<<3)+j)<<1], mod_symbs[0][(((i<<3)+j)<<1)+1]);
printf("PDSCH Modulation: Qm %d(%d)\n", Qm, nb_symbols);
for (int i=0; i<nb_symbols>>3; i++) {
for (int j=0; j<8; j++) {
printf("%d %d\t", mod_symbs[0][((i<<3)+j)<<1], mod_symbs[0][(((i<<3)+j)<<1)+1]);
}
printf("\n");
}
printf("\n");
}
#endif
/// Layer mapping
nr_layer_mapping(mod_symbs,
rel15->nb_layers,
nb_symbols,
tx_layers);
rel15->nrOfLayers,
nb_symbols,
tx_layers);
#ifdef DEBUG_DLSCH
printf("Layer mapping (%d layers):\n", rel15->nb_layers);
for (int l=0; l<rel15->nb_layers; l++)
for (int i=0; i<(nb_symbols/rel15->nb_layers)>>3; i++) {
for (int j=0; j<8; j++) {
printf("%d %d\t", tx_layers[l][((i<<3)+j)<<1], tx_layers[l][(((i<<3)+j)<<1)+1]);
printf("Layer mapping (%d layers):\n", rel15->nrOfLayers);
for (int l=0; l<rel15->nrOfLayers; l++)
for (int i=0; i<(nb_symbols/rel15->nrOfLayers)>>3; i++) {
for (int j=0; j<8; j++) {
printf("%d %d\t", tx_layers[l][((i<<3)+j)<<1], tx_layers[l][(((i<<3)+j)<<1)+1]);
}
printf("\n");
}
printf("\n");
}
#endif
/// Antenna port mapping
//to be moved to init phase potentially, for now tx_layers 1-8 are mapped on antenna ports 1000-1007
/// DMRS QPSK modulation
uint16_t n_dmrs = ((rel15->n_prb+rel15->start_prb)*rel15->nb_re_dmrs)<<1;
uint8_t dmrs_Type = rel15->dmrsConfigType;
int nb_re_dmrs = (dmrs_Type==1) ? 6:4;
uint16_t n_dmrs = ((rel15->rbSize+rel15->rbStart)*nb_re_dmrs)<<1;
int16_t mod_dmrs[n_dmrs<<1];
uint8_t dmrs_type = rel15->dmrs_Type;
uint8_t mapping_type = rel15->mapping_type;
l0 = get_l0(mapping_type, 2);//config->pdsch_config.dmrs_typeA_position.value);
l0 = get_l0(rel15->dlDmrsSymbPos);
nr_modulation(pdsch_dmrs[l0][0], n_dmrs, DMRS_MOD_ORDER, mod_dmrs); // currently only codeword 0 is modulated. Qm = 2 as DMRS is QPSK modulated
#ifdef DEBUG_DLSCH
printf("DMRS modulation (single symbol %d, %d symbols, type %d):\n", l0, n_dmrs>>1, dmrs_type);
printf("DMRS modulation (single symbol %d, %d symbols, type %d):\n", l0, n_dmrs>>1, dmrs_Type);
for (int i=0; i<n_dmrs>>4; i++) {
for (int j=0; j<8; j++) {
printf("%d %d\t", mod_dmrs[((i<<3)+j)<<1], mod_dmrs[(((i<<3)+j)<<1)+1]);
......@@ -199,21 +205,21 @@ for (int i=0; i<n_dmrs>>4; i++) {
/// Resource mapping
// Non interleaved VRB to PRB mapping
uint16_t start_sc = frame_parms->first_carrier_offset + rel15->start_prb*NR_NB_SC_PER_RB;
uint16_t start_sc = frame_parms->first_carrier_offset + rel15->rbStart*NR_NB_SC_PER_RB;
if (start_sc >= frame_parms->ofdm_symbol_size)
start_sc -= frame_parms->ofdm_symbol_size;
#ifdef DEBUG_DLSCH_MAPPING
printf("PDSCH resource mapping started (start SC %d\tstart symbol %d\tN_PRB %d\tnb_symbols %d)\n",
start_sc, rel15->start_symbol, rel15->n_prb, rel15->nb_symbols);
start_sc, rel15->StartSymbolIndex, rel15->rbSize, rel15->NrOfSymbols);
#endif
for (int ap=0; ap<rel15->nb_layers; ap++) {
for (int ap=0; ap<rel15->nrOfLayers; ap++) {
// DMRS params for this ap
get_Wt(Wt, ap, dmrs_type);
get_Wf(Wf, ap, dmrs_type);
delta = get_delta(ap, dmrs_type);
get_Wt(Wt, ap, dmrs_Type);
get_Wf(Wf, ap, dmrs_Type);
delta = get_delta(ap, dmrs_Type);
l_prime[0] = 0; // single symbol ap 0
uint8_t dmrs_symbol = l0+l_prime[0];
#ifdef DEBUG_DLSCH_MAPPING
......@@ -222,15 +228,15 @@ ap, Wt[0], Wt[1], Wf[0], Wf[1], delta, l_prime[0], l0, dmrs_symbol);
#endif
uint8_t k_prime=0;
uint16_t m=0, n=0, dmrs_idx=0, k=0;
if (dmrs_type == NFAPI_NR_DMRS_TYPE1) // another if condition to be included to check pdsch config type (reference of k)
dmrs_idx = rel15->start_prb*6;
if (dmrs_Type == NFAPI_NR_DMRS_TYPE1) // another if condition to be included to check pdsch config type (reference of k)
dmrs_idx = rel15->rbStart*6;
else
dmrs_idx = rel15->start_prb*4;
dmrs_idx = rel15->rbStart*4;
for (int l=rel15->start_symbol; l<rel15->start_symbol+rel15->nb_symbols; l++) {
for (int l=rel15->StartSymbolIndex; l<rel15->StartSymbolIndex+rel15->NrOfSymbols; l++) {
k = start_sc;
for (int i=0; i<rel15->n_prb*NR_NB_SC_PER_RB; i++) {
if ((l == dmrs_symbol) && (k == ((start_sc+get_dmrs_freq_idx(n, k_prime, delta, dmrs_type))%(frame_parms->ofdm_symbol_size)))) {
for (int i=0; i<rel15->rbSize*NR_NB_SC_PER_RB; i++) {
if ((l == dmrs_symbol) && (k == ((start_sc+get_dmrs_freq_idx(n, k_prime, delta, dmrs_Type))%(frame_parms->ofdm_symbol_size)))) {
((int16_t*)txdataF[ap])[(l*frame_parms->ofdm_symbol_size + k)<<1] = (Wt[l_prime[0]]*Wf[k_prime]*amp*mod_dmrs[dmrs_idx<<1]) >> 15;
((int16_t*)txdataF[ap])[((l*frame_parms->ofdm_symbol_size + k)<<1) + 1] = (Wt[l_prime[0]]*Wf[k_prime]*amp*mod_dmrs[(dmrs_idx<<1) + 1]) >> 15;
#ifdef DEBUG_DLSCH_MAPPING
......
......@@ -79,6 +79,7 @@ uint8_t nr_generate_pdsch(NR_gNB_DLSCH_t *dlsch,
int frame,
uint8_t slot,
NR_DL_FRAME_PARMS *frame_parms,
int xOverhead,
nfapi_nr_config_request_t *config,
time_stats_t *dlsch_encoding_stats,
time_stats_t *dlsch_scrambling_stats,
......
......@@ -281,11 +281,11 @@ int nr_dlsch_encoding(unsigned char *a,
uint8_t harq_pid = dlsch->harq_ids[frame&2][slot];
AssertFatal(harq_pid<8 && harq_pid>=0,"illegal harq_pid %d\b",harq_pid);
nfapi_nr_dl_config_dlsch_pdu_rel15_t *rel15 = &dlsch->harq_processes[harq_pid]->dlsch_pdu.dlsch_pdu_rel15;
uint16_t nb_rb = rel15->n_prb;
uint8_t nb_symb_sch = rel15->nb_symbols;
uint16_t nb_rb = rel15->rbSize;
uint8_t nb_symb_sch = rel15->NrOfSymbols;
uint32_t A, Z, Kb, F=0;
uint32_t *Zc = &Z;
uint8_t mod_order = rel15->modulation_order;
uint8_t mod_order = rel15->qamModOrder[0];
uint16_t Kr=0,r;
uint32_t r_offset=0;
//uint8_t *d_tmp[MAX_NUM_DLSCH_SEGMENTS];
......@@ -293,9 +293,9 @@ int nr_dlsch_encoding(unsigned char *a,
uint32_t E;
uint8_t Ilbrm = 1;
uint32_t Tbslbrm = 950984; //max tbs
uint8_t nb_re_dmrs = rel15->dmrs_Type==1 ? 6:4;
uint16_t length_dmrs = rel15->dmrs_maxLength;
uint16_t R=rel15->coding_rate;
uint8_t nb_re_dmrs = rel15->dmrsConfigType==1 ? 6:4;
uint16_t length_dmrs = get_num_dmrs(rel15->dlDmrsSymbPos);
uint16_t R=rel15->targetCodeRate[0];
float Coderate = 0.0;
uint8_t Nl = 4;
......@@ -308,9 +308,9 @@ int nr_dlsch_encoding(unsigned char *a,
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_ENB_DLSCH_ENCODING, VCD_FUNCTION_IN);
A = rel15->transport_block_size;
A = rel15->TBSize[0]<<3;
G = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs,mod_order,rel15->nb_layers);
G = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs,mod_order,rel15->nrOfLayers);
LOG_D(PHY,"dlsch coding A %d G %d mod_order %d\n", A,G, mod_order);
......@@ -435,16 +435,16 @@ int nr_dlsch_encoding(unsigned char *a,
#endif
#ifdef DEBUG_DLSCH_CODING
LOG_D(PHY,"rvidx in encoding = %d\n", rel15->redundancy_version);
LOG_D(PHY,"rvidx in encoding = %d\n", rel15->rvIndex[0]);
#endif
E = nr_get_E(G, dlsch->harq_processes[harq_pid]->C, mod_order, rel15->nb_layers, r);
E = nr_get_E(G, dlsch->harq_processes[harq_pid]->C, mod_order, rel15->nrOfLayers, r);
// for tbslbrm calculation according to 5.4.2.1 of 38.212
if (rel15.nb_layers < Nl)
Nl = rel15.nb_layers;
if (rel15->nrOfLayers < Nl)
Nl = rel15->nrOfLayers;
Tbslbrm = nr_compute_tbslbrm(rel15.mcs_table,nb_rb,Nl,dlsch->harq_processes[harq_pid]->C);
Tbslbrm = nr_compute_tbslbrm(rel15->mcsTable[0],nb_rb,Nl,dlsch->harq_processes[harq_pid]->C);
nr_rate_matching_ldpc(Ilbrm,
Tbslbrm,
......@@ -453,7 +453,7 @@ int nr_dlsch_encoding(unsigned char *a,
dlsch->harq_processes[harq_pid]->d[r],
dlsch->harq_processes[harq_pid]->e+r_offset,
dlsch->harq_processes[harq_pid]->C,
rel15->redundancy_version,
rel15->rvIndex[0],
E);
#ifdef DEBUG_DLSCH_CODING
......
......@@ -27,6 +27,7 @@
* \company Eurecom
* \email: desouza@eurecom.fr
* \note
* \warning
*/
......@@ -46,6 +47,7 @@ uint8_t nr_pdsch_default_time_alloc_C_L[15] = {2,2,2,2,2,4,4,4,4,4,7,12,11,6,6};
/// Time domain allocation routines
/*
void nr_get_time_domain_allocation_type(nfapi_nr_config_request_t config,
nfapi_nr_dl_config_dci_dl_pdu dci_pdu,
nfapi_nr_dl_config_dlsch_pdu *dlsch_pdu) {
......@@ -73,9 +75,9 @@ void nr_get_time_domain_allocation_type(nfapi_nr_config_request_t config,
case NFAPI_NR_RNTI_RA:
case NFAPI_NR_RNTI_TC:
/*AssertFatal(dci_alloc.pdcch_params.common_search_space_type == NFAPI_NR_COMMON_SEARCH_SPACE_TYPE_1,
"Invalid common search space type %d for RNTI %d, expected %d\n",dci_alloc.pdcch_params.common_search_space_type,
NFAPI_NR_COMMON_SEARCH_SPACE_TYPE_1, dci_alloc.rnti_type);*/
//AssertFatal(dci_alloc.pdcch_params.common_search_space_type == NFAPI_NR_COMMON_SEARCH_SPACE_TYPE_1,
//"Invalid common search space type %d for RNTI %d, expected %d\n",dci_alloc.pdcch_params.common_search_space_type,
//NFAPI_NR_COMMON_SEARCH_SPACE_TYPE_1, dci_alloc.rnti_type);
*alloc_type = (alloc_list_flag) ? NFAPI_NR_PDSCH_TIME_DOMAIN_ALLOC_TYPE_ALLOC_LIST : NFAPI_NR_PDSCH_TIME_DOMAIN_ALLOC_TYPE_DEFAULT_A;
break;
......@@ -94,6 +96,7 @@ void nr_get_time_domain_allocation_type(nfapi_nr_config_request_t config,
}
}
uint16_t get_SLIV(uint8_t S, uint8_t L) {
return ( (uint16_t)(((L-1)<=7)? (14*(L-1)+S) : (14*(15-L)+(13-S))) );
}
......@@ -104,7 +107,7 @@ static inline uint8_t get_K0(uint8_t row_idx, uint8_t time_alloc_type) {
((row_idx==6)||(row_idx==7)||(row_idx==15))? 1 : 0);
}
/*ideally combine the calculation of L in the same function once the right struct is defined*/
// ideally combine the calculation of L in the same function once the right struct is defined
uint8_t nr_get_S(uint8_t row_idx, uint8_t CP, uint8_t time_alloc_type, uint8_t dmrs_TypeA_Position) {
uint8_t idx;
......@@ -132,6 +135,7 @@ uint8_t nr_get_S(uint8_t row_idx, uint8_t CP, uint8_t time_alloc_type, uint8_t d
return 0; // temp warning fix
}
void nr_check_time_alloc(uint8_t S, uint8_t L,nfapi_nr_dl_config_dlsch_pdu_rel15_t *rel15,nfapi_nr_config_request_t *cfg) {
switch (cfg->subframe_config.dl_cyclic_prefix_type.value) {
......@@ -241,6 +245,7 @@ void nr_get_PRG_parms(NR_BWP_PARMS* bwp, NR_gNB_DCI_ALLOC_t dci_alloc, uint8_t p
LOG_I(PHY, "PRG parameters for BWP %d location %d N_RB %d:\n", bwp->bwp_id, bwp->location, bwp->N_RB);
LOG_I(PHY, "P_prime %d\t start size %d\t endsize %d\t N_PRG %d\n", prg_parms->P_prime, prg_parms->start_size, prg_parms->end_size, prg_parms->N_PRG);
}
*/
/// Payload emulation
void nr_emulate_dlsch_payload(uint8_t* pdu, uint16_t size) {
......
......@@ -90,8 +90,14 @@ uint16_t get_dmrs_freq_idx(uint16_t n, uint8_t k_prime, uint8_t delta, uint8_t d
return dmrs_idx;
}
uint8_t get_l0(uint8_t mapping_type, uint8_t dmrs_typeA_position) {
return ((mapping_type==NFAPI_NR_PDSCH_MAPPING_TYPE_A)?dmrs_typeA_position:0);
uint8_t get_l0(uint16_t dlDmrsSymbPos) {
uint16_t mask=dlDmrsSymbPos;
int l0;
for (l0=0;l0<14;l0++) {
if ((mask&1) == 1) break;
mask>>=1;
}
AssertFatal(l0 < 4,"impossible l0 %d\n",l0);
return (l0);
}
......@@ -47,4 +47,4 @@ uint8_t get_delta(uint8_t ap, uint8_t config);
uint16_t get_dmrs_freq_idx(uint16_t n, uint8_t k_prime, uint8_t delta, uint8_t dmrs_type);
uint8_t get_l0(uint8_t mapping_type, uint8_t dmrs_typeA_position);
uint8_t get_l0(uint16_t dlDmrsSymbPos);
......@@ -33,165 +33,6 @@
#include "nr_transport_common_proto.h"
#include "PHY/CODING/coding_defs.h"
uint8_t nr_get_Qm_dl(uint8_t Imcs, uint8_t table_idx) {
switch(table_idx) {
case 0:
return (Table_51311[Imcs][0]);
break;
case 1:
return (Table_51312[Imcs][0]);
break;
case 2:
return (Table_51313[Imcs][0]);
break;
default:
AssertFatal(0, "Invalid MCS table index %d (expected in range [1,3])\n", table_idx);
}
}
uint32_t nr_get_code_rate_dl(uint8_t Imcs, uint8_t table_idx) {
switch(table_idx) {
case 0:
return (Table_51311[Imcs][1]);
break;
case 1:
return (Table_51312[Imcs][1]);
break;
case 2:
return (Table_51313[Imcs][1]);
break;
default:
AssertFatal(0, "Invalid MCS table index %d (expected in range [1,3])\n", table_idx);
}
}
uint8_t nr_get_Qm_ul(uint8_t Imcs, uint8_t table_idx) {
switch(table_idx) {
case 0:
return (Table_51311[Imcs][0]);
break;
case 1:
return (Table_51312[Imcs][0]);
break;
case 2:
return (Table_51313[Imcs][0]);
break;
case 3:
return (Table_61411[Imcs][0]);
break;
case 4:
return (Table_61412[Imcs][0]);
break;
default:
AssertFatal(0, "Invalid MCS table index %d (expected in range [1,2])\n", table_idx);
}
}
uint32_t nr_get_code_rate_ul(uint8_t Imcs, uint8_t table_idx) {
switch(table_idx) {
case 0:
return (Table_51311[Imcs][1]);
break;
case 1:
return (Table_51312[Imcs][1]);
break;
case 2:
return (Table_51313[Imcs][1]);
break;
case 3:
return (Table_61411[Imcs][1]);
break;
case 4:
return (Table_61412[Imcs][1]);
break;
default:
AssertFatal(0, "Invalid MCS table index %d (expected in range [1,2])\n", table_idx);
}
}
static inline uint8_t is_codeword_disabled(uint8_t format, uint8_t Imcs, uint8_t rv) {
return ((format==NFAPI_NR_DL_DCI_FORMAT_1_1)&&(Imcs==26)&&(rv==1));
}
static inline uint8_t get_table_idx(uint8_t mcs_table, uint8_t dci_format, uint8_t rnti_type, uint8_t ss_type) {
if ((mcs_table == NFAPI_NR_MCS_TABLE_QAM256) && (dci_format == NFAPI_NR_DL_DCI_FORMAT_1_1) && ((rnti_type==NFAPI_NR_RNTI_C)||(rnti_type==NFAPI_NR_RNTI_CS)))
return 2;
else if ((mcs_table == NFAPI_NR_MCS_TABLE_QAM64_LOW_SE) && (rnti_type!=NFAPI_NR_RNTI_new) && (rnti_type==NFAPI_NR_RNTI_C) && (ss_type==NFAPI_NR_SEARCH_SPACE_TYPE_UE_SPECIFIC))
return 3;
else if (rnti_type==NFAPI_NR_RNTI_new)
return 3;
else if ((mcs_table == NFAPI_NR_MCS_TABLE_QAM256) && (rnti_type==NFAPI_NR_RNTI_CS) && (dci_format == NFAPI_NR_DL_DCI_FORMAT_1_1))
return 2; // Condition mcs_table not configured in sps_config necessary here but not yet implemented
/*else if((mcs_table == NFAPI_NR_MCS_TABLE_QAM64_LOW_SE) && (rnti_type==NFAPI_NR_RNTI_CS))
* table_idx = 3;
* Note: the commented block refers to the case where the mcs_table is from sps_config*/
else
return 1;
}
void nr_get_tbs_dl(nfapi_nr_dl_config_dlsch_pdu *dlsch_pdu,
nfapi_nr_dl_config_dci_dl_pdu dci_pdu,
nfapi_nr_config_request_t config) {
LOG_D(MAC, "TBS calculation\n");
nfapi_nr_dl_config_pdcch_parameters_rel15_t params_rel15 = dci_pdu.pdcch_params_rel15;
nfapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_rel15 = &dlsch_pdu->dlsch_pdu_rel15;
uint8_t rnti_type = params_rel15.rnti_type;
uint16_t N_PRB_oh = ((rnti_type==NFAPI_NR_RNTI_SI)||(rnti_type==NFAPI_NR_RNTI_RA)||(rnti_type==NFAPI_NR_RNTI_P))? 0 : \
(config.pdsch_config.x_overhead.value);
uint8_t N_PRB_DMRS = (config.pdsch_config.dmrs_type.value == NFAPI_NR_DMRS_TYPE1)?6:4; //This only works for antenna port 1000
uint8_t N_sh_symb = dlsch_rel15->nb_symbols;
uint8_t Imcs = dlsch_rel15->mcs_idx;
uint16_t N_RE_prime = NR_NB_SC_PER_RB*N_sh_symb - N_PRB_DMRS - N_PRB_oh;
LOG_D(MAC, "N_RE_prime %d for %d symbols %d DMRS per PRB and %d overhead\n", N_RE_prime, N_sh_symb, N_PRB_DMRS, N_PRB_oh);
uint16_t R;
uint32_t TBS=0;
uint8_t table_idx, Qm;
/*uint8_t mcs_table = config.pdsch_config.mcs_table.value;
uint8_t ss_type = params_rel15.search_space_type;
uint8_t dci_format = params_rel15.dci_format;
get_table_idx(mcs_table, dci_format, rnti_type, ss_type);*/
table_idx = 0;
R = nr_get_code_rate_dl(Imcs, table_idx);
Qm = nr_get_Qm_dl(Imcs, table_idx);
TBS = nr_compute_tbs(Qm,
R,
dlsch_rel15->n_prb,
N_sh_symb,
N_PRB_DMRS,
N_PRB_oh,
dlsch_rel15->nb_layers);
dlsch_rel15->coding_rate = R;
dlsch_rel15->modulation_order = Qm;
dlsch_rel15->transport_block_size = TBS;
dlsch_rel15->nb_re_dmrs = N_PRB_DMRS;
dlsch_rel15->nb_mod_symbols = N_RE_prime*dlsch_rel15->n_prb*dlsch_rel15->nb_codewords;
dlsch_rel15->mcs_table = table_idx;
LOG_D(MAC, "TBS %d : N_PRB_DMRS %d N_sh_symb %d N_PRB_oh %d R %d Qm %d table %d nb_symbols %d\n",
TBS, N_PRB_DMRS, N_sh_symb, N_PRB_oh, R, Qm, table_idx, dlsch_rel15->nb_mod_symbols);
}
uint32_t nr_get_G(uint16_t nb_rb, uint16_t nb_symb_sch,uint8_t nb_re_dmrs,uint16_t length_dmrs, uint8_t Qm, uint8_t Nl) {
uint32_t G;
......
......@@ -43,17 +43,9 @@
#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
/** \brief Computes Q based on I_MCS PDSCH and table_idx for downlink. Implements MCS Tables from 38.214. */
uint8_t nr_get_Qm_dl(uint8_t Imcs, uint8_t table_idx);
uint32_t nr_get_code_rate_dl(uint8_t Imcs, uint8_t table_idx);
/** \brief Computes Q based on I_MCS PDSCH and table_idx for uplink. Implements MCS Tables from 38.214. */
uint8_t nr_get_Qm_ul(uint8_t Imcs, uint8_t table_idx);
uint32_t nr_get_code_rate_ul(uint8_t Imcs, uint8_t table_idx);
void nr_get_tbs_dl(nfapi_nr_dl_config_dlsch_pdu *dlsch_pdu,
nfapi_nr_dl_config_dci_dl_pdu dci_pdu,
nfapi_nr_config_request_t config);
/** \brief Computes available bits G. */
uint32_t nr_get_G(uint16_t nb_rb, uint16_t nb_symb_sch, uint8_t nb_re_dmrs, uint16_t length_dmrs, uint8_t Qm, uint8_t Nl);
......
......@@ -213,7 +213,6 @@ int nr_pbch_channel_estimation(PHY_VARS_NR_UE *ue,
int16_t ch[2],*pil,*rxF,*dl_ch,*fl,*fm,*fr;
int ch_offset,symbol_offset;
int slot_pbch;
fapi_nr_pbch_config_t *pbch_config = &ue->nrUE_config.pbch_config;
// initialized to 5ms in nr_init_ue for scenarios where UE is not configured (otherwise acquired by cell configuration from gNB or LTE)
uint8_t ssb_periodicity = 10;// ue->ssb_periodicity;
......
......@@ -247,16 +247,11 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
nfapi_nr_dl_config_dlsch_pdu_rel15_t *dl_config_pdu = &harq_process->dl_config_pdu;
uint8_t dmrs_Type = dl_config_pdu->dmrs_Type;
uint8_t dmrs_Type = dl_config_pdu->dmrsConfigType;
AssertFatal(dmrs_Type == 1 || dmrs_Type == 2,"Illegal dmrs_type %d\n",dmrs_Type);
uint8_t nb_re_dmrs = (dmrs_Type==1)?6:4;
uint8_t dmrs_TypeA_Position = dl_config_pdu->dmrs_TypeA_Position;
AssertFatal(dmrs_TypeA_Position == 2 || dmrs_TypeA_Position == 3,"Illegal dmrs_TypeA_Position %d\n",dmrs_TypeA_Position);
uint16_t dmrs_maxLength = dl_config_pdu->dmrs_maxLength;
AssertFatal(dmrs_maxLength == 1 || dmrs_maxLength == 2,"Illegal dmrs_maxLength %d\n",dmrs_maxLength);
uint16_t dmrs_AdditionalPosition = dl_config_pdu->dmrs_AdditionalPosition;
AssertFatal(dmrs_AdditionalPosition >= 0 && dmrs_AdditionalPosition <= 4,"Illegal dmrs_additional_symbols %d\n",dmrs_AdditionalPosition);
uint16_t dmrs_length = get_num_dmrs(dl_config_pdu->dlDmrsSymbPos);
AssertFatal(dmrs_length == 1 || dmrs_length == 2,"Illegal dmrs_length %d\n",dmrs_length);
uint32_t i,j;
......@@ -311,13 +306,13 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
uint16_t nb_rb_oh = 0; // it was not computed at UE side even before and set to 0 in nr_compute_tbs
harq_process->TBS = nr_compute_tbs(harq_process->Qm,harq_process->R,nb_rb,nb_symb_sch,nb_re_dmrs*length_dmrs, nb_rb_oh, harq_process->Nl);
harq_process->TBS = nr_compute_tbs(harq_process->Qm,harq_process->R,nb_rb,nb_symb_sch,nb_re_dmrs*dmrs_length, nb_rb_oh, harq_process->Nl);
A = harq_process->TBS;
ret = dlsch->max_ldpc_iterations + 1;
dlsch->last_iteration_cnt = ret;
harq_process->G = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, harq_process->Qm,harq_process->Nl);
harq_process->G = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, dmrs_length, harq_process->Qm,harq_process->Nl);
G = harq_process->G;
LOG_I(PHY,"DLSCH Decoding, harq_pid %d TBS %d G %d mcs %d Nl %d nb_symb_sch %d nb_rb %d\n",harq_pid,A,G, harq_process->mcs, harq_process->Nl, nb_symb_sch,nb_rb);
......@@ -788,10 +783,10 @@ uint32_t nr_dlsch_decoding_mthread(PHY_VARS_NR_UE *phy_vars_ue,
uint32_t Tbslbrm = 950984;
uint16_t nb_rb = 30;
double Coderate = 0.0;
nfapi_nr_config_request_t *cfg = &phy_vars_ue->nrUE_config;
uint8_t dmrs_type = cfg->pdsch_config.dmrs_type.value;
nfapi_nr_dl_config_dlsch_pdu_rel15_t *dl_config_pdu = &harq_processes[harq_pid]->dl_config_pdu
uint8_t dmrs_type = dl_config_pdu->dmrsConfigType;
uint8_t nb_re_dmrs = (dmrs_type==NFAPI_NR_DMRS_TYPE1)?6:4;
uint16_t length_dmrs = 1; //cfg->pdsch_config.dmrs_max_length.value;
uint16_t length_dmrs = get_num_dmrs(dl_config_pdu->dlDmrsSymbPos);
uint32_t i,j;
......
......@@ -182,12 +182,12 @@ void nr_schedule_response(NR_Sched_Rsp_t *Sched_INFO){
{
nfapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_pdu_rel15 = &dl_config_pdu->dlsch_pdu.dlsch_pdu_rel15;
uint16_t pdu_index = dlsch_pdu_rel15->pdu_index;
uint16_t pduIndex = dlsch_pdu_rel15->pduIndex;
uint16_t tx_pdus = TX_req->tx_request_body.number_of_pdus;
uint16_t invalid_pdu = pdu_index == -1;
uint8_t *sdu = invalid_pdu ? NULL : pdu_index >= tx_pdus ? NULL : TX_req->tx_request_body.tx_pdu_list[pdu_index].segments[0].segment_data;
uint16_t invalid_pdu = pduIndex == -1;
uint8_t *sdu = invalid_pdu ? NULL : pduIndex >= tx_pdus ? NULL : TX_req->tx_request_body.tx_pdu_list[pduIndex].segments[0].segment_data;
AssertFatal(sdu!=NULL,"sdu is null, pdu_index %d, tx_pdus %d\n",pdu_index,tx_pdus);
AssertFatal(sdu!=NULL,"sdu is null, pduIndex %d, tx_pdus %d\n",pduIndex,tx_pdus);
handle_nr_nfapi_dlsch_pdu(gNB,frame,slot,&dl_config_pdu->dlsch_pdu, sdu);
do_oai=1;
}
......
......@@ -200,7 +200,9 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
&gNB->pdcch_vars.dci_alloc[0],
gNB->nr_gold_pdsch_dmrs[slot],
gNB->common_vars.txdataF,
AMP, frame, slot, fp, cfg,
AMP, frame, slot, fp,
0, // xOverhead
cfg,
&gNB->dlsch_encoding_stats,
&gNB->dlsch_scrambling_stats,
&gNB->dlsch_modulation_stats);
......@@ -226,7 +228,7 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
if (do_prach_rx(fp,frame,slot)) L1_nr_prach_procedures(gNB,frame,slot/fp->slots_per_subframe);
*/
void nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int UE_id, uint8_t harq_pid)
void nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ULSCH_id, uint8_t harq_pid)
{
NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
nfapi_nr_ul_config_ulsch_pdu *rel15_ul = &gNB->ulsch[ULSCH_id+1][0]->harq_processes[harq_pid]->ulsch_pdu;
......@@ -371,7 +373,7 @@ void phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx,
for (ULSCH_id = 0; ULSCH_id < /*NUMBER_OF_NR_ULSCH_MAX*/1; ULSCH_id++) { // temporary set to 1 untill list of connected UEs is implemented
for(symbol = symbol_start; symbol < symbol_end; symbol++) {
nr_rx_pusch(gNB, UE_id, frame_rx, slot_rx, symbol, harq_pid);
nr_rx_pusch(gNB, ULSCH_id, frame_rx, slot_rx, symbol, harq_pid);
}
nr_ulsch_procedures(gNB, frame_rx, slot_rx, ULSCH_id, harq_pid);
nr_fill_rx_indication(gNB, frame_rx, slot_rx, ULSCH_id, harq_pid); // indicate SDU to MAC
......
......@@ -373,7 +373,7 @@ void nr_compute_srs_pos(lte_frame_type_t frameType,uint16_t isrs,uint16_t *psrsP
void set_tx_harq_id(NR_UE_ULSCH_t *ulsch, int harq_pid, int slot_tx);
int get_tx_harq_id(NR_UE_ULSCH_t *ulsch, int slot_tx);
int is_pbch_in_slot(fapi_nr_pbch_config_t *pbch_config, int frame, int slot, int periodicity, uint16_t slots_per_frame);
fapi_nr_dl_config_bch_pdu *is_pbch_in_slot(fapi_nr_dl_config_request_t *DLconfigreq, int frame, int slot, int periodicity, uint16_t slots_per_frame);
/*@}*/
......
This diff is collapsed.
......@@ -53,8 +53,6 @@
uint8_t is_cqi_TXOp(PHY_VARS_NR_UE *ue,UE_nr_rxtx_proc_t *proc,uint8_t gNB_id);
uint8_t is_ri_TXOp(PHY_VARS_NR_UE *ue,UE_nr_rxtx_proc_t *proc,uint8_t gNB_id);
/*
void nr_generate_pucch0(int32_t **txdataF,
NR_DL_FRAME_PARMS *frame_parms,
......@@ -111,8 +109,9 @@ void nr_generate_pucch3_4(int32_t **txdataF,
/**************** functions **************************************/
//extern uint8_t is_cqi_TXOp(PHY_VARS_NR_UE *ue,UE_nr_rxtx_proc_t *proc,uint8_t eNB_id);
//extern uint8_t is_ri_TXOp(PHY_VARS_NR_UE *ue,UE_nr_rxtx_proc_t *proc,uint8_t eNB_id);
uint8_t is_nr_cqi_TXOp(PHY_VARS_NR_UE *ue,UE_nr_rxtx_proc_t *proc,uint8_t eNB_id) { return(0); }
uint8_t is_nr_ri_TXOp(PHY_VARS_NR_UE *ue,UE_nr_rxtx_proc_t *proc,uint8_t eNB_id) { return(0);}
/*******************************************************************
*
* NAME : pucch_procedures_ue_nr
......@@ -227,10 +226,10 @@ bool pucch_procedures_ue_nr(PHY_VARS_NR_UE *ue, uint8_t gNB_id, UE_nr_rxtx_proc_
&n_HARQ_ACK, reset_harq); // 1 to reset ACK/NACK status : 0 otherwise
cqi_status = ((ue->cqi_report_config[gNB_id].CQI_ReportPeriodic.cqi_PMI_ConfigIndex>0) &&
(is_cqi_TXOp(ue,proc,gNB_id) == 1));
(is_nr_cqi_TXOp(ue,proc,gNB_id) == 1));
ri_status = ((ue->cqi_report_config[gNB_id].CQI_ReportPeriodic.ri_ConfigIndex>0) &&
(is_ri_TXOp(ue,proc,gNB_id) == 1));
(is_nr_ri_TXOp(ue,proc,gNB_id) == 1));
csi_status = get_csi_nr(ue, gNB_id, &csi_payload);
......
This diff is collapsed.
......@@ -57,25 +57,6 @@ int32_t uplink_frequency_offset[MAX_NUM_CCs][4];
double cpuf;
int nfapi_mode=0;
int oai_nfapi_hi_dci0_req(nfapi_hi_dci0_request_t *hi_dci0_req) { return(0);}
int oai_nfapi_tx_req(nfapi_tx_request_t *tx_req) { return(0); }
int oai_nfapi_dl_config_req(nfapi_dl_config_request_t *dl_config_req) { return(0); }
int oai_nfapi_ul_config_req(nfapi_ul_config_request_t *ul_config_req) { return(0); }
int oai_nfapi_nr_dl_config_req(nfapi_nr_dl_config_request_t *dl_config_req) {return(0);}
uint32_t from_nrarfcn(int nr_bandP,uint32_t dl_nrarfcn) {return(0);}
int32_t get_nr_uldl_offset(int nr_bandP) {return(0);}
NR_IF_Module_t *NR_IF_Module_init(int Mod_id){return(NULL);}
void exit_function(const char* file, const char* function, const int line,const char *s) {
const char * msg= s==NULL ? "no comment": s;
printf("Exiting at: %s:%d %s(), %s\n", file, line, function, msg);
exit(-1);
}
uint16_t NB_UE_INST = 1;
......
......@@ -54,6 +54,7 @@ void config_common(int Mod_idP,
NR_ServingCellConfigCommon_t *scc
){
nfapi_nr_config_request_t *cfg = &RC.nrmac[Mod_idP]->config[0];
RC.nrmac[Mod_idP]->common_channels[0].ServingCellConfigCommon = scc;
int mu = 1;
......
......@@ -357,7 +357,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP,
*/
PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, ENB_FLAG_YES,NOT_A_RNTI, frameP, slotP,module_idP);
// pdcp_run(&ctxt);
pdcp_run(&ctxt);
//rrc_rx_tx(&ctxt, CC_id);
// This schedules MIB
......@@ -371,7 +371,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP,
nr_schedule_css_dlsch_phytest(module_idP, frameP, slotP);*/
if (slotP==1)
nr_schedule_uss_dlsch_phytest(module_idP, frameP, slotP);
nr_schedule_uss_dlsch_phytest(module_idP, frameP, slotP, NULL);
/*
// Allocate CCEs for good after scheduling is done
......
......@@ -63,13 +63,14 @@ void nr_schedule_css_dlsch_phytest(module_id_t module_idP,
frame_t frameP,
sub_frame_t subframeP);
int configure_fapi_dl_Tx(nfapi_nr_dl_config_request_body_t *dl_req,
nfapi_tx_request_pdu_t *TX_req,
nfapi_nr_config_request_t *cfg,
nfapi_nr_coreset_t* coreset,
nfapi_nr_search_space_t* search_space,
int16_t pdu_index,
nfapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config);
int configure_fapi_dl_Tx(int Mod_id,
nfapi_nr_dl_config_request_body_t *dl_req,
nfapi_tx_request_pdu_t *TX_req,
nfapi_nr_config_request_t *cfg,
nfapi_nr_coreset_t* coreset,
nfapi_nr_search_space_t* search_space,
int16_t pdu_index);
void nr_schedule_uss_dlsch_phytest(module_id_t module_idP,
frame_t frameP,
......@@ -97,8 +98,7 @@ int nr_is_dci_opportunity(nfapi_nr_search_space_t search_space,
void nr_configure_dci_from_pdcch_config(nfapi_nr_dl_config_pdcch_parameters_rel15_t* pdcch_params,
nfapi_nr_coreset_t* coreset,
nfapi_nr_search_space_t* search_space,
nfapi_nr_config_request_t cfg,
uint16_t N_RB);
NR_BWP_Downlink_t *bwp);
int get_dlscs(nfapi_nr_config_request_t *cfg);
......@@ -117,8 +117,16 @@ uint64_t from_nrarfcn(int nr_bandP, uint32_t dl_nrarfcn);
uint32_t to_nrarfcn(int nr_bandP, uint64_t dl_CarrierFreq, uint32_t bw);
void nr_get_tbs(nfapi_nr_dl_config_dlsch_pdu *dlsch_pdu,
nfapi_nr_dl_config_dci_dl_pdu dci_pdu);
void nr_get_tbs_dl(nfapi_nr_dl_config_dlsch_pdu *dlsch_pdu,
nfapi_nr_dl_config_dci_dl_pdu dci_pdu,
int x_overhead);
/** \brief Computes Q based on I_MCS PDSCH and table_idx for downlink. Implements MCS Tables from 38.214. */
uint8_t nr_get_Qm_dl(uint8_t Imcs, uint8_t table_idx);
uint32_t nr_get_code_rate_dl(uint8_t Imcs, uint8_t table_idx);
/** \brief Computes Q based on I_MCS PDSCH and table_idx for uplink. Implements MCS Tables from 38.214. */
uint8_t nr_get_Qm_ul(uint8_t Imcs, uint8_t table_idx);
uint32_t nr_get_code_rate_ul(uint8_t Imcs, uint8_t table_idx);
int NRRIV2BW(int locationAndBandwidth,int N_RB);
......@@ -135,4 +143,8 @@ find_nr_UE_id(module_id_t mod_idP,
int add_new_nr_ue(module_id_t mod_idP,
rnti_t rntiP);
int get_num_dmrs(uint16_t dmrs_mask );
int16_t fill_dmrs_mask(NR_PDSCH_Config_t *pdsch_Config,int dmrs_TypeA_Position,int NrOfSymbols);
#endif /*__LAYER2_NR_MAC_PROTO_H__*/
......@@ -44,9 +44,6 @@
extern RAN_CONTEXT_t RC;
void set_cset_offset(uint16_t offset_bits) {
RC.nrmac[0]->coreset[0][1].frequency_domain_resources >>= offset_bits;
}
void mac_top_init_gNB(void)
{
......
......@@ -20,7 +20,7 @@
*/
/*! \file nr_mac_common.c
* \brief Common MAC functions for NR UE and gNB
* \brief Common MAC/PHY functions for NR UE and gNB
* \author Florian Kaltenberger and Raymond Knopp
* \date 2019
* \version 0.1
......@@ -160,7 +160,12 @@ uint64_t from_nrarfcn(int nr_bandP,
for (i = 0; i < 31 && nr_bandtable[i].band != nr_bandP; i++);
AssertFatal(dl_nrarfcn>=nr_bandtable[i].N_OFFs_DL,"dl_nrarfcn %u < N_OFFs_DL %llu\n",dl_nrarfcn, (long long unsigned int)nr_bandtable[i].N_OFFs_DL);
LOG_I(PHY,"Computing dl_frequency (pointA %d => %llu (dlmin %llu, nr_bandtable[%d].N_OFFs_DL %d))\n",dl_nrarfcn,1000*(nr_bandtable[i].dl_min + (dl_nrarfcn - nr_bandtable[i].N_OFFs_DL) * deltaFglobal),nr_bandtable[i].dl_min,i,nr_bandtable[i].N_OFFs_DL);
LOG_I(PHY,"Computing dl_frequency (pointA %llu => %llu (dlmin %llu, nr_bandtable[%d].N_OFFs_DL %llu))\n",
(unsigned long long)dl_nrarfcn,
(unsigned long long)(1000*(nr_bandtable[i].dl_min + (dl_nrarfcn - nr_bandtable[i].N_OFFs_DL) * deltaFglobal)),
(unsigned long long)nr_bandtable[i].dl_min,
i,
(unsigned long long)nr_bandtable[i].N_OFFs_DL);
return 1000*(nr_bandtable[i].dl_min + (dl_nrarfcn - nr_bandtable[i].N_OFFs_DL) * deltaFglobal);
}
......@@ -176,3 +181,194 @@ int32_t get_nr_uldl_offset(int nr_bandP)
return (nr_bandtable[i].dl_min - nr_bandtable[i].ul_min);
}
void nr_get_tbs_dl(nfapi_nr_dl_config_dlsch_pdu *dlsch_pdu,
nfapi_nr_dl_config_dci_dl_pdu dci_pdu,
int x_overhead) {
LOG_D(MAC, "TBS calculation\n");
nfapi_nr_dl_config_pdcch_parameters_rel15_t params_rel15 = dci_pdu.pdcch_params_rel15;
nfapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_rel15 = &dlsch_pdu->dlsch_pdu_rel15;
uint8_t rnti_type = params_rel15.rnti_type;
uint16_t N_PRB_oh = ((rnti_type==NFAPI_NR_RNTI_SI)||(rnti_type==NFAPI_NR_RNTI_RA)||(rnti_type==NFAPI_NR_RNTI_P))? 0 : \
(x_overhead);
uint8_t N_PRB_DMRS = (dlsch_rel15->dmrsConfigType == NFAPI_NR_DMRS_TYPE1)?6:4; //This only works for antenna port 1000
uint8_t N_sh_symb = dlsch_rel15->NrOfSymbols;
uint8_t Imcs = dlsch_rel15->mcsIndex[0];
uint16_t N_RE_prime = NR_NB_SC_PER_RB*N_sh_symb - N_PRB_DMRS - N_PRB_oh;
LOG_D(MAC, "N_RE_prime %d for %d symbols %d DMRS per PRB and %d overhead\n", N_RE_prime, N_sh_symb, N_PRB_DMRS, N_PRB_oh);
uint16_t R;
uint32_t TBS=0;
uint8_t table_idx, Qm;
/*uint8_t mcs_table = config.pdsch_config.mcs_table.value;
uint8_t ss_type = params_rel15.search_space_type;
uint8_t dci_format = params_rel15.dci_format;
get_table_idx(mcs_table, dci_format, rnti_type, ss_type);*/
table_idx = 0;
R = nr_get_code_rate_dl(Imcs, table_idx);
Qm = nr_get_Qm_dl(Imcs, table_idx);
TBS = nr_compute_tbs(Qm,
R,
dlsch_rel15->rbSize,
N_sh_symb,
N_PRB_DMRS,
N_PRB_oh,
dlsch_rel15->nrOfLayers);
dlsch_rel15->targetCodeRate[0] = R;
dlsch_rel15->qamModOrder[0] = Qm;
dlsch_rel15->TBSize[0] = TBS;
// dlsch_rel15->nb_mod_symbols = N_RE_prime*dlsch_rel15->n_prb*dlsch_rel15->nb_codewords;
dlsch_rel15->mcsTable[0] = table_idx;
LOG_D(MAC, "TBS %d : N_PRB_DMRS %d N_sh_symb %d N_PRB_oh %d R %d Qm %d table %d nb_symbols %d\n",
TBS, N_PRB_DMRS, N_sh_symb, N_PRB_oh, R, Qm, table_idx,N_RE_prime*dlsch_rel15->rbSize*dlsch_rel15->NrOfCodewords );
}
//Table 5.1.3.1-1 of 38.214
uint16_t Table_51311[29][2] = {{2,120},{2,157},{2,193},{2,251},{2,308},{2,379},{2,449},{2,526},{2,602},{2,679},{4,340},{4,378},{4,434},{4,490},{4,553},{4,616},
{4,658},{6,438},{6,466},{6,517},{6,567},{6,616},{6,666},{6,719},{6,772},{6,822},{6,873}, {6,910}, {6,948}};
//Table 5.1.3.1-2 of 38.214
// Imcs values 20 and 26 have been multiplied by 2 to avoid the floating point
uint16_t Table_51312[28][2] = {{2,120},{2,193},{2,308},{2,449},{2,602},{4,378},{4,434},{4,490},{4,553},{4,616},{4,658},{6,466},{6,517},{6,567},{6,616},{6,666},
{6,719},{6,772},{6,822},{6,873},{8,1365},{8,711},{8,754},{8,797},{8,841},{8,885},{8,1833},{8,948}};
//Table 5.1.3.1-3 of 38.214
uint16_t Table_51313[29][2] = {{2,30},{2,40},{2,50},{2,64},{2,78},{2,99},{2,120},{2,157},{2,193},{2,251},{2,308},{2,379},{2,449},{2,526},{2,602},{4,340},
{4,378},{4,434},{4,490},{4,553},{4,616},{6,438},{6,466},{6,517},{6,567},{6,616},{6,666}, {6,719}, {6,772}};
//Table 6.1.4.1-1 of 38.214 TODO fix for tp-pi2BPSK
uint16_t Table_61411[28][2] = {{2,120},{2,157},{2,193},{2,251},{2,308},{2,379},{2,449},{2,526},{2,602},{2,679},{4,340},{4,378},{4,434},{4,490},{4,553},{4,616},
{4,658},{6,466},{6,517},{6,567},{6,616},{6,666},{6,719},{6,772},{6,822},{6,873}, {6,910}, {6,948}};
//Table 6.1.4.1-2 of 38.214 TODO fix for tp-pi2BPSK
uint16_t Table_61412[28][2] = {{2,30},{2,40},{2,50},{2,64},{2,78},{2,99},{2,120},{2,157},{2,193},{2,251},{2,308},{2,379},{2,449},{2,526},{2,602},{2,679},
{4,378},{4,434},{4,490},{4,553},{4,616},{4,658},{4,699},{4,772},{6,567},{6,616},{6,666}, {6,772}};
uint8_t nr_get_Qm_dl(uint8_t Imcs, uint8_t table_idx) {
switch(table_idx) {
case 0:
return (Table_51311[Imcs][0]);
break;
case 1:
return (Table_51312[Imcs][0]);
break;
case 2:
return (Table_51313[Imcs][0]);
break;
default:
AssertFatal(0, "Invalid MCS table index %d (expected in range [1,3])\n", table_idx);
}
}
uint32_t nr_get_code_rate_dl(uint8_t Imcs, uint8_t table_idx) {
switch(table_idx) {
case 0:
return (Table_51311[Imcs][1]);
break;
case 1:
return (Table_51312[Imcs][1]);
break;
case 2:
return (Table_51313[Imcs][1]);
break;
default:
AssertFatal(0, "Invalid MCS table index %d (expected in range [1,3])\n", table_idx);
}
}
uint8_t nr_get_Qm_ul(uint8_t Imcs, uint8_t table_idx) {
switch(table_idx) {
case 0:
return (Table_51311[Imcs][0]);
break;
case 1:
return (Table_51312[Imcs][0]);
break;
case 2:
return (Table_51313[Imcs][0]);
break;
case 3:
return (Table_61411[Imcs][0]);
break;
case 4:
return (Table_61412[Imcs][0]);
break;
default:
AssertFatal(0, "Invalid MCS table index %d (expected in range [1,2])\n", table_idx);
}
}
uint32_t nr_get_code_rate_ul(uint8_t Imcs, uint8_t table_idx) {
switch(table_idx) {
case 0:
return (Table_51311[Imcs][1]);
break;
case 1:
return (Table_51312[Imcs][1]);
break;
case 2:
return (Table_51313[Imcs][1]);
break;
case 3:
return (Table_61411[Imcs][1]);
break;
case 4:
return (Table_61412[Imcs][1]);
break;
default:
AssertFatal(0, "Invalid MCS table index %d (expected in range [1,2])\n", table_idx);
}
}
static inline uint8_t is_codeword_disabled(uint8_t format, uint8_t Imcs, uint8_t rv) {
return ((format==NFAPI_NR_DL_DCI_FORMAT_1_1)&&(Imcs==26)&&(rv==1));
}
static inline uint8_t get_table_idx(uint8_t mcs_table, uint8_t dci_format, uint8_t rnti_type, uint8_t ss_type) {
if ((mcs_table == NFAPI_NR_MCS_TABLE_QAM256) && (dci_format == NFAPI_NR_DL_DCI_FORMAT_1_1) && ((rnti_type==NFAPI_NR_RNTI_C)||(rnti_type==NFAPI_NR_RNTI_CS)))
return 2;
else if ((mcs_table == NFAPI_NR_MCS_TABLE_QAM64_LOW_SE) && (rnti_type!=NFAPI_NR_RNTI_new) && (rnti_type==NFAPI_NR_RNTI_C) && (ss_type==NFAPI_NR_SEARCH_SPACE_TYPE_UE_SPECIFIC))
return 3;
else if (rnti_type==NFAPI_NR_RNTI_new)
return 3;
else if ((mcs_table == NFAPI_NR_MCS_TABLE_QAM256) && (rnti_type==NFAPI_NR_RNTI_CS) && (dci_format == NFAPI_NR_DL_DCI_FORMAT_1_1))
return 2; // Condition mcs_table not configured in sps_config necessary here but not yet implemented
/*else if((mcs_table == NFAPI_NR_MCS_TABLE_QAM64_LOW_SE) && (rnti_type==NFAPI_NR_RNTI_CS))
* table_idx = 3;
* Note: the commented block refers to the case where the mcs_table is from sps_config*/
else
return 1;
}
int get_num_dmrs(uint16_t dmrs_mask ) {
int num_dmrs;
for (int i=0;i<16;i++) num_dmrs+=((dmrs_mask>>i)&1);
return(num_dmrs);
}
......@@ -62,37 +62,38 @@
/*! \brief gNB common channels */
typedef struct {
int physCellId;
int p_gNB;
int Ncp;
int nr_band;
uint32_t dl_CarrierFreq;
NR_BCCH_BCH_Message_t *mib;
NR_TDD_UL_DL_ConfigCommon_t *tdd_Config;
NR_ARFCN_ValueEUTRA_t ul_CarrierFreq;
long ul_Bandwidth;
/// Outgoing MIB PDU for PHY
MIB_PDU MIB_pdu;
/// Outgoing BCCH pdu for PHY
BCCH_PDU BCCH_pdu;
/// Outgoing BCCH DCI allocation
uint32_t BCCH_alloc_pdu;
/// Outgoing CCCH pdu for PHY
CCCH_PDU CCCH_pdu;
/// Outgoing PCCH DCI allocation
uint32_t PCCH_alloc_pdu;
/// Outgoing PCCH pdu for PHY
PCCH_PDU PCCH_pdu;
/// Outgoing RAR pdu for PHY
RAR_PDU RAR_pdu;
/// Template for RA computations
RA_t ra[NB_RA_PROC_MAX];
/// VRB map for common channels
uint8_t vrb_map[100];
/// VRB map for common channels and retransmissions by PHICH
uint8_t vrb_map_UL[100];
/// number of subframe allocation pattern available for MBSFN sync area
uint8_t num_sf_allocation_pattern;
int physCellId;
int p_gNB;
int Ncp;
int nr_band;
uint32_t dl_CarrierFreq;
NR_BCCH_BCH_Message_t *mib;
NR_ServingCellConfigCommon_t *ServingCellConfigCommon;
NR_TDD_UL_DL_ConfigCommon_t *tdd_Config;
NR_ARFCN_ValueEUTRA_t ul_CarrierFreq;
long ul_Bandwidth;
/// Outgoing MIB PDU for PHY
MIB_PDU MIB_pdu;
/// Outgoing BCCH pdu for PHY
BCCH_PDU BCCH_pdu;
/// Outgoing BCCH DCI allocation
uint32_t BCCH_alloc_pdu;
/// Outgoing CCCH pdu for PHY
CCCH_PDU CCCH_pdu;
/// Outgoing PCCH DCI allocation
uint32_t PCCH_alloc_pdu;
/// Outgoing PCCH pdu for PHY
PCCH_PDU PCCH_pdu;
/// Outgoing RAR pdu for PHY
RAR_PDU RAR_pdu;
/// Template for RA computations
RA_t ra[NB_RA_PROC_MAX];
/// VRB map for common channels
uint8_t vrb_map[100];
/// VRB map for common channels and retransmissions by PHICH
uint8_t vrb_map_UL[100];
/// number of subframe allocation pattern available for MBSFN sync area
uint8_t num_sf_allocation_pattern;
} NR_COMMON_channels_t;
/*! \brief scheduling control information set through an API (not used)*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment