831 lines
27 KiB
C
831 lines
27 KiB
C
/*
|
|
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The OpenAirInterface Software Alliance licenses this file to You under
|
|
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.openairinterface.org/?page_id=698
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*-------------------------------------------------------------------------------
|
|
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
|
* contact@openairinterface.org
|
|
*/
|
|
|
|
#include "nr_modulation.h"
|
|
#include "PHY/NR_REFSIG/nr_mod_table.h"
|
|
#include "executables/softmodem-common.h"
|
|
|
|
// #define DEBUG_DLSCH_PRECODING_PRINT_WITH_TRIVIAL // TODO: For debug, to be removed if want to merge to develop
|
|
|
|
//Table 6.3.1.5-1 Precoding Matrix W 1 layer 2 antenna ports 'n' = -1 and 'o' = -j
|
|
const char nr_W_1l_2p[6][2][1] = {
|
|
{{'1'}, {'0'}}, // pmi 0
|
|
{{'0'}, {'1'}},
|
|
{{'1'}, {'1'}},
|
|
{{'1'}, {'n'}},
|
|
{{'1'}, {'j'}},
|
|
{{'1'}, {'o'}} // pmi 5
|
|
};
|
|
|
|
//Table 6.3.1.5-3 Precoding Matrix W 1 layer 4 antenna ports 'n' = -1 and 'o' = -j
|
|
const char nr_W_1l_4p[28][4][1] = {
|
|
{{'1'}, {'0'}, {'0'}, {'0'}}, // pmi 0
|
|
{{'0'}, {'1'}, {'0'}, {'0'}},
|
|
{{'0'}, {'0'}, {'1'}, {'0'}},
|
|
{{'0'}, {'0'}, {'0'}, {'1'}},
|
|
{{'1'}, {'0'}, {'1'}, {'0'}},
|
|
{{'1'}, {'0'}, {'n'}, {'0'}},
|
|
{{'1'}, {'0'}, {'j'}, {'0'}},
|
|
{{'1'}, {'0'}, {'o'}, {'0'}}, // pmi 7
|
|
{{'0'}, {'1'}, {'0'}, {'1'}}, // pmi 8
|
|
{{'0'}, {'1'}, {'0'}, {'n'}},
|
|
{{'0'}, {'1'}, {'0'}, {'j'}},
|
|
{{'0'}, {'1'}, {'0'}, {'o'}},
|
|
{{'1'}, {'1'}, {'1'}, {'1'}},
|
|
{{'1'}, {'1'}, {'j'}, {'j'}},
|
|
{{'1'}, {'1'}, {'n'}, {'n'}},
|
|
{{'1'}, {'1'}, {'o'}, {'o'}},
|
|
{{'1'}, {'j'}, {'1'}, {'j'}}, // pmi
|
|
// 16
|
|
{{'1'}, {'j'}, {'j'}, {'n'}},
|
|
{{'1'}, {'j'}, {'n'}, {'o'}},
|
|
{{'1'}, {'j'}, {'o'}, {'1'}},
|
|
{{'1'}, {'n'}, {'1'}, {'n'}},
|
|
{{'1'}, {'n'}, {'j'}, {'o'}},
|
|
{{'1'}, {'n'}, {'n'}, {'1'}},
|
|
{{'1'}, {'n'}, {'o'}, {'j'}}, // pmi 23
|
|
{{'1'}, {'o'}, {'1'}, {'o'}}, // pmi 24
|
|
{{'1'}, {'o'}, {'j'}, {'1'}},
|
|
{{'1'}, {'o'}, {'n'}, {'j'}},
|
|
{{'1'}, {'o'}, {'o'}, {'n'}} // pmi 27
|
|
};
|
|
|
|
//Table 6.3.1.5-4 Precoding Matrix W 2 antenna ports layers 2 'n' = -1 and 'o' = -j
|
|
const char nr_W_2l_2p[3][2][2] = {
|
|
{{'1', '0'}, {'0', '1'}}, // pmi 0
|
|
{{'1', '1'}, {'1', 'n'}},
|
|
{{'1', '1'}, {'j', 'o'}} // pmi 2
|
|
};
|
|
|
|
//Table 6.3.1.5-5 Precoding Matrix W 2 layers 4 antenna ports 'n' = -1 and 'o' = -j
|
|
const char nr_W_2l_4p[22][4][2] = {
|
|
{{'1', '0'}, {'0', '1'}, {'0', '0'}, {'0', '0'}}, // pmi 0
|
|
{{'1', '0'}, {'0', '0'}, {'0', '1'}, {'0', '0'}}, {{'1', '0'}, {'0', '0'}, {'0', '0'}, {'0', '1'}},
|
|
{{'0', '0'}, {'1', '0'}, {'0', '1'}, {'0', '0'}}, // pmi 3
|
|
{{'0', '0'}, {'1', '0'}, {'0', '0'}, {'0', '1'}}, // pmi 4
|
|
{{'0', '0'}, {'0', '0'}, {'1', '0'}, {'0', '1'}}, {{'1', '0'}, {'0', '1'}, {'1', '0'}, {'0', 'o'}},
|
|
{{'1', '0'}, {'0', '1'}, {'1', '0'}, {'0', 'j'}}, {{'1', '0'}, {'0', '1'}, {'o', '0'}, {'0', '1'}}, // pmi 8
|
|
{{'1', '0'}, {'0', '1'}, {'o', '0'}, {'0', 'n'}}, {{'1', '0'}, {'0', '1'}, {'n', '0'}, {'0', 'o'}},
|
|
{{'1', '0'}, {'0', '1'}, {'n', '0'}, {'0', 'j'}}, // pmi 11
|
|
{{'1', '0'}, {'0', '1'}, {'j', '0'}, {'0', '1'}}, // pmi 12
|
|
{{'1', '0'}, {'0', '1'}, {'j', '0'}, {'0', 'n'}}, {{'1', '1'}, {'1', '1'}, {'1', 'n'}, {'1', 'n'}},
|
|
{{'1', '1'}, {'1', '1'}, {'j', 'o'}, {'j', 'o'}}, // pmi 15
|
|
{{'1', '1'}, {'j', 'j'}, {'1', 'n'}, {'j', 'o'}}, // pmi 16
|
|
{{'1', '1'}, {'j', 'j'}, {'j', 'o'}, {'n', '1'}}, {{'1', '1'}, {'n', 'n'}, {'1', 'n'}, {'n', '1'}},
|
|
{{'1', '1'}, {'n', 'n'}, {'j', 'o'}, {'o', 'j'}}, // pmi 19
|
|
{{'1', '1'}, {'o', 'o'}, {'1', 'n'}, {'o', 'j'}}, {{'1', '1'}, {'o', 'o'}, {'j', 'o'}, {'1', 'n'}} // pmi 21
|
|
};
|
|
|
|
//Table 6.3.1.5-6 Precoding Matrix W 3 layers 4 antenna ports 'n' = -1 and 'o' = -j
|
|
const char nr_W_3l_4p[7][4][3] = {{{'1', '0', '0'}, {'0', '1', '0'}, {'0', '0', '1'}, {'0', '0', '0'}}, // pmi 0
|
|
{{'1', '0', '0'}, {'0', '1', '0'}, {'1', '0', '0'}, {'0', '0', '1'}},
|
|
{{'1', '0', '0'}, {'0', '1', '0'}, {'n', '0', '0'}, {'0', '0', '1'}},
|
|
{{'1', '1', '1'}, {'1', 'n', '1'}, {'1', '1', 'n'}, {'1', 'n', 'n'}}, // pmi 3
|
|
{{'1', '1', '1'}, {'1', 'n', '1'}, {'j', 'j', 'o'}, {'j', 'o', 'o'}}, // pmi 4
|
|
{{'1', '1', '1'}, {'n', '1', 'n'}, {'1', '1', 'n'}, {'n', '1', '1'}},
|
|
{{'1', '1', '1'}, {'n', '1', 'n'}, {'j', 'j', 'o'}, {'o', 'j', 'j'}}};
|
|
|
|
//Table 6.3.1.5-7 Precoding Matrix W 4 layers 4 antenna ports 'n' = -1 and 'o' = -j
|
|
const char nr_W_4l_4p[5][4][4] = {
|
|
{{'1', '0', '0', '0'}, {'0', '1', '0', '0'}, {'0', '0', '1', '0'}, {'0', '0', '0', '1'}}, // pmi 0
|
|
{{'1', '1', '0', '0'}, {'0', '0', '1', '1'}, {'1', 'n', '0', '0'}, {'0', '0', '1', 'n'}},
|
|
{{'1', '1', '0', '0'}, {'0', '0', '1', '1'}, {'j', 'o', '0', '0'}, {'0', '0', 'j', 'o'}},
|
|
{{'1', '1', '1', '1'}, {'1', 'n', '1', 'n'}, {'1', '1', 'n', 'n'}, {'1', 'n', 'n', '1'}}, // pmi 3
|
|
{{'1', '1', '1', '1'}, {'1', 'n', '1', 'n'}, {'j', 'j', 'o', 'o'}, {'j', 'o', 'o', 'j'}} // pmi 4
|
|
};
|
|
|
|
void nr_modulation(const uint32_t *in,
|
|
uint32_t length,
|
|
uint16_t mod_order,
|
|
int16_t *out)
|
|
{
|
|
uint16_t mask = ((1<<mod_order)-1);
|
|
int32_t* nr_mod_table32;
|
|
int32_t* out32 = (int32_t*) out;
|
|
const uint8_t *in_bytes = (const uint8_t *)in;
|
|
const uint64_t *in64 = (const uint64_t *)in;
|
|
int64_t* out64 = (int64_t*) out;
|
|
uint32_t i=0;
|
|
|
|
#if defined(__SSE2__)
|
|
simde__m128i *nr_mod_table128;
|
|
simde__m128i *out128;
|
|
#endif
|
|
|
|
LOG_D(PHY,"nr_modulation: length %d, mod_order %d\n",length,mod_order);
|
|
|
|
switch (mod_order) {
|
|
|
|
#if defined(__SSE2__)
|
|
case 2:
|
|
nr_mod_table128 = (simde__m128i *)nr_qpsk_byte_mod_table;
|
|
out128 = (simde__m128i *)out;
|
|
for (i=0; i<length/8; i++)
|
|
out128[i] = nr_mod_table128[in_bytes[i]];
|
|
// the bits that are left out
|
|
i = i*8/2;
|
|
nr_mod_table32 = (int32_t*) nr_qpsk_mod_table;
|
|
while (i<length/2) {
|
|
const int idx = ((in_bytes[(i * 2) / 8] >> ((i * 2) & 0x7)) & mask);
|
|
out32[i] = nr_mod_table32[idx];
|
|
i++;
|
|
}
|
|
return;
|
|
#else
|
|
case 2:
|
|
nr_mod_table32 = (int32_t*) nr_qpsk_mod_table;
|
|
for (i=0; i<length/mod_order; i++) {
|
|
const int idx = ((in[i * 2 / 32] >> ((i * 2) & 0x1f)) & mask);
|
|
out32[i] = nr_mod_table32[idx];
|
|
}
|
|
return;
|
|
#endif
|
|
|
|
case 4:
|
|
out64 = (int64_t*) out;
|
|
for (i=0; i<length/8; i++)
|
|
out64[i] = nr_16qam_byte_mod_table[in_bytes[i]];
|
|
// the bits that are left out
|
|
i = i*8/4;
|
|
while (i<length/4) {
|
|
const int idx = ((in_bytes[(i * 4) / 8] >> ((i * 4) & 0x7)) & mask);
|
|
out32[i] = nr_16qam_mod_table[idx];
|
|
i++;
|
|
}
|
|
return;
|
|
|
|
case 6:
|
|
if (length > (3*64))
|
|
for (i = 0; i < length - 3 * 64; i += 3 * 64) {
|
|
uint64_t x = *in64++;
|
|
uint64_t x1 = x & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x1 = (x >> 12) & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x1 = (x >> 24) & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x1 = (x >> 36) & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x1 = (x >> 48) & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
uint64_t x2 = (x >> 60);
|
|
x = *in64++;
|
|
x2 |= x<<4;
|
|
x1 = x2 & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x1 = (x2 >> 12) & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x1 = (x2 >> 24) & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x1 = (x2 >> 36) & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x1 = (x2 >> 48) & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x2 = ((x>>56)&0xf0) | (x2>>60);
|
|
x = *in64++;
|
|
x2 |= x<<8;
|
|
x1 = x2 & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x1 = (x2 >> 12) & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x1 = (x2 >> 24) & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x1 = (x2 >> 36) & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x1 = (x2 >> 48) & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x2 = ((x>>52)&0xff0) | (x2>>60);
|
|
*out64++ = nr_64qam_mod_table[x2];
|
|
}
|
|
|
|
while (i + 24 <= length) {
|
|
uint32_t xx = 0;
|
|
memcpy(&xx, in_bytes + i / 8, 3);
|
|
uint64_t x1 = xx & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
x1 = (xx >> 12) & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
i += 24;
|
|
}
|
|
if (i != length) {
|
|
uint32_t xx = 0;
|
|
memcpy(&xx, in_bytes + i / 8, 2);
|
|
uint64_t x1 = xx & 0xfff;
|
|
*out64++ = nr_64qam_mod_table[x1];
|
|
}
|
|
return;
|
|
|
|
case 8:
|
|
nr_mod_table32 = (int32_t*) nr_256qam_mod_table;
|
|
for (i=0; i<length/8; i++)
|
|
out32[i] = nr_mod_table32[in_bytes[i]];
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
AssertFatal(false,"Invalid or unsupported modulation order %d\n",mod_order);
|
|
}
|
|
|
|
void nr_layer_mapping(int nbCodes,
|
|
int encoded_len,
|
|
c16_t mod_symbs[nbCodes][encoded_len],
|
|
uint8_t n_layers,
|
|
int layerSz,
|
|
uint32_t n_symbs,
|
|
c16_t tx_layer[layerSz],
|
|
int layer)
|
|
{
|
|
LOG_D(PHY,"Doing layer mapping for %d layers, %d symbols\n",n_layers,n_symbs);
|
|
|
|
switch (n_layers) {
|
|
|
|
case 1:
|
|
memcpy(tx_layer, mod_symbs[0], n_symbs * sizeof(**mod_symbs));
|
|
break;
|
|
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
for (int i = 0; i < n_symbs / n_layers; i++) {
|
|
const c16_t *base = mod_symbs[0] + n_layers * i;
|
|
tx_layer[i] = base[layer];
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
if (layer < 2)
|
|
for (int i = 0; i < n_symbs; i += 2) {
|
|
const int txIdx = i / 2;
|
|
tx_layer[txIdx] = mod_symbs[0][i + layer];
|
|
}
|
|
else
|
|
for (int i = 0; i < n_symbs; i += 3) {
|
|
const int txIdx = i / 3;
|
|
tx_layer[txIdx] = mod_symbs[1][i + layer];
|
|
}
|
|
break;
|
|
|
|
case 6:
|
|
for (int q=0; q<2; q++)
|
|
for (int i = 0; i < n_symbs; i += 3) {
|
|
const int txIdx = i / 3;
|
|
tx_layer[txIdx] = mod_symbs[q][i + layer];
|
|
}
|
|
break;
|
|
|
|
case 7:
|
|
if (layer < 3)
|
|
for (int i = 0; i < n_symbs; i += 3) {
|
|
const int txIdx = i / 3;
|
|
tx_layer[txIdx] = mod_symbs[1][i + layer];
|
|
}
|
|
else
|
|
for (int i = 0; i < n_symbs; i += 4) {
|
|
const int txIdx = i / 4;
|
|
tx_layer[txIdx] = mod_symbs[0][i + layer];
|
|
}
|
|
break;
|
|
|
|
case 8:
|
|
for (int q=0; q<2; q++)
|
|
for (int i = 0; i < n_symbs; i += 4) {
|
|
const int txIdx = i / 4;
|
|
tx_layer[txIdx] = mod_symbs[q][i + layer];
|
|
}
|
|
break;
|
|
|
|
default:
|
|
AssertFatal(0, "Invalid number of layers %d\n", n_layers);
|
|
}
|
|
}
|
|
|
|
void nr_ue_layer_mapping(const c16_t *mod_symbs, const int n_layers, const int n_symbs, int sz, c16_t tx_layers[][sz])
|
|
{
|
|
for (int i=0; i<n_symbs/n_layers; i++) {
|
|
for (int l=0; l<n_layers; l++) {
|
|
tx_layers[l][i] = c16mulRealShift(mod_symbs[n_layers * i + l], AMP, 15);
|
|
}
|
|
}
|
|
}
|
|
|
|
void nr_dft(c16_t *z, c16_t *d, uint32_t Msc_PUSCH)
|
|
{
|
|
simde__m128i dft_in128[3240], dft_out128[3240];
|
|
c16_t *dft_in0 = (c16_t *)dft_in128, *dft_out0 = (c16_t *)dft_out128;
|
|
|
|
uint32_t i, ip;
|
|
|
|
simde__m128i norm128;
|
|
|
|
if ((Msc_PUSCH % 1536) > 0) {
|
|
for (i = 0, ip = 0; i < Msc_PUSCH; i++, ip+=4) {
|
|
dft_in0[ip] = d[i];
|
|
}
|
|
}
|
|
|
|
switch (Msc_PUSCH) {
|
|
case 12:
|
|
dft(DFT_12,(int16_t *)dft_in0, (int16_t *)dft_out0,0);
|
|
|
|
norm128 = simde_mm_set1_epi16(9459);
|
|
for (i=0; i<12; i++) {
|
|
((simde__m128i*)dft_out0)[i] = simde_mm_slli_epi16(simde_mm_mulhi_epi16(((simde__m128i*)dft_out0)[i], norm128), 1);
|
|
}
|
|
|
|
break;
|
|
|
|
case 24:
|
|
dft(DFT_24,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 36:
|
|
dft(DFT_36,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 48:
|
|
dft(DFT_48,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 60:
|
|
dft(DFT_60,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 72:
|
|
dft(DFT_72,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 96:
|
|
dft(DFT_96,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 108:
|
|
dft(DFT_108,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 120:
|
|
dft(DFT_120,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 144:
|
|
dft(DFT_144,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 180:
|
|
dft(DFT_180,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 192:
|
|
dft(DFT_192,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 216:
|
|
dft(DFT_216,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 240:
|
|
dft(DFT_240,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 288:
|
|
dft(DFT_288,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 300:
|
|
dft(DFT_300,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 324:
|
|
dft(DFT_324,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 360:
|
|
dft(DFT_360,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 384:
|
|
dft(DFT_384,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 432:
|
|
dft(DFT_432,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 480:
|
|
dft(DFT_480,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 540:
|
|
dft(DFT_540,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 576:
|
|
dft(DFT_576,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 600:
|
|
dft(DFT_600,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 648:
|
|
dft(DFT_648,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 720:
|
|
dft(DFT_720,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 768:
|
|
dft(DFT_768,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 864:
|
|
dft(DFT_864,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 900:
|
|
dft(DFT_900,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 960:
|
|
dft(DFT_960,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 972:
|
|
dft(DFT_972,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 1080:
|
|
dft(DFT_1080,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 1152:
|
|
dft(DFT_1152,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 1200:
|
|
dft(DFT_1200,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 1296:
|
|
dft(DFT_1296,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 1440:
|
|
dft(DFT_1440,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 1500:
|
|
dft(DFT_1500,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 1536:
|
|
//dft(DFT_1536,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
dft(DFT_1536,(int16_t*)d, (int16_t*)z, 1);
|
|
break;
|
|
|
|
case 1620:
|
|
dft(DFT_1620,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 1728:
|
|
dft(DFT_1728,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 1800:
|
|
dft(DFT_1800,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 1920:
|
|
dft(DFT_1920,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 1944:
|
|
dft(DFT_1944,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 2160:
|
|
dft(DFT_2160,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 2304:
|
|
dft(DFT_2304,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 2400:
|
|
dft(DFT_2400,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 2592:
|
|
dft(DFT_2592,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 2700:
|
|
dft(DFT_2700,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 2880:
|
|
dft(DFT_2880,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 2916:
|
|
dft(DFT_2916,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 3000:
|
|
dft(DFT_3000,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
case 3072:
|
|
//dft(DFT_3072,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
dft(DFT_3072,(int16_t*)d, (int16_t*)z, 1);
|
|
break;
|
|
|
|
case 3240:
|
|
dft(DFT_3240,(int16_t*)dft_in0, (int16_t*)dft_out0, 1);
|
|
break;
|
|
|
|
default:
|
|
// should not be reached
|
|
LOG_E( PHY, "Unsupported Msc_PUSCH value of %"PRIu16"\n", Msc_PUSCH );
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
if ((Msc_PUSCH % 1536) > 0) {
|
|
for (i = 0, ip = 0; i < Msc_PUSCH; i++, ip+=4)
|
|
z[i] = dft_out0[ip];
|
|
}
|
|
|
|
}
|
|
|
|
void perform_symbol_rotation(NR_DL_FRAME_PARMS *fp, double f0, c16_t *symbol_rotation)
|
|
{
|
|
const int nsymb = fp->symbols_per_slot * fp->slots_per_frame/10;
|
|
const double Tc=(1/480e3/4096);
|
|
const double Nu=2048*64*(1/(float)(1<<fp->numerology_index));
|
|
const double Ncp0=16*64 + (144*64*(1/(float)(1<<fp->numerology_index)));
|
|
const double Ncp1=(144*64*(1/(float)(1<<fp->numerology_index)));
|
|
|
|
LOG_I(PHY, "Doing symbol rotation calculation for TX/RX, f0 %f Hz, Nsymb %d\n", f0, nsymb);
|
|
|
|
double tl = 0.0;
|
|
double poff = 0.0;
|
|
double exp_re = 0.0;
|
|
double exp_im = 0.0;
|
|
|
|
for (int l = 0; l < nsymb; l++) {
|
|
double Ncp;
|
|
if (l == 0 || l == (7 * (1 << fp->numerology_index))) {
|
|
Ncp = Ncp0;
|
|
} else {
|
|
Ncp = Ncp1;
|
|
}
|
|
|
|
poff = 2 * M_PI * (tl + (Ncp * Tc)) * f0;
|
|
exp_re = cos(poff);
|
|
exp_im = sin(-poff);
|
|
symbol_rotation[l].r = (int16_t)floor(exp_re * 32767);
|
|
symbol_rotation[l].i = (int16_t)floor(exp_im * 32767);
|
|
|
|
LOG_D(PHY,
|
|
"Symbol rotation %d/%d => tl %f (%d,%d) (%f)\n",
|
|
l,
|
|
nsymb,
|
|
tl,
|
|
symbol_rotation[l].r,
|
|
symbol_rotation[l].i,
|
|
(poff / 2 / M_PI) - floor(poff / 2 / M_PI));
|
|
|
|
tl += (Nu + Ncp) * Tc;
|
|
}
|
|
}
|
|
|
|
void init_symbol_rotation(NR_DL_FRAME_PARMS *fp)
|
|
{
|
|
double f[2] = {(double)fp->dl_CarrierFreq, (double)fp->ul_CarrierFreq};
|
|
|
|
for (int ll = 0; ll < 2; ll++) {
|
|
double f0 = f[ll];
|
|
if (f0 == 0)
|
|
continue;
|
|
c16_t *rot = fp->symbol_rotation[ll];
|
|
|
|
perform_symbol_rotation(fp, f0, rot);
|
|
}
|
|
}
|
|
|
|
void init_timeshift_rotation(NR_DL_FRAME_PARMS *fp)
|
|
{
|
|
const int sample_offset = fp->nb_prefix_samples / fp->ofdm_offset_divisor;
|
|
for (int i = 0; i < fp->ofdm_symbol_size; i++) {
|
|
double poff = -i * 2.0 * M_PI * sample_offset / fp->ofdm_symbol_size;
|
|
double exp_re = cos(poff);
|
|
double exp_im = sin(-poff);
|
|
fp->timeshift_symbol_rotation[i].r = (int16_t)round(exp_re * 32767);
|
|
fp->timeshift_symbol_rotation[i].i = (int16_t)round(exp_im * 32767);
|
|
|
|
if (i < 10)
|
|
LOG_D(PHY,"Timeshift symbol rotation %d => (%d,%d) %f\n",i,
|
|
fp->timeshift_symbol_rotation[i].r,
|
|
fp->timeshift_symbol_rotation[i].i,
|
|
poff);
|
|
}
|
|
}
|
|
|
|
c16_t nr_layer_precoder(int sz, c16_t datatx_F_precoding[][sz], const char *prec_matrix, uint8_t n_layers, int32_t re_offset)
|
|
{
|
|
c16_t precodatatx_F = {0};
|
|
|
|
for (int al = 0; al<n_layers; al++) {
|
|
c16_t antenna = datatx_F_precoding[al][re_offset];
|
|
switch (prec_matrix[al]) {
|
|
case '0': //multiply by zero
|
|
break;
|
|
|
|
case '1': //multiply by 1
|
|
precodatatx_F = c16add(precodatatx_F, antenna);
|
|
break;
|
|
|
|
case 'n': // multiply by -1
|
|
precodatatx_F = c16sub(precodatatx_F, antenna);
|
|
break;
|
|
|
|
case 'j': //
|
|
precodatatx_F.r -= antenna.i;
|
|
precodatatx_F.i += antenna.r;
|
|
break;
|
|
|
|
case 'o': // -j
|
|
precodatatx_F.r += antenna.i;
|
|
precodatatx_F.i -= antenna.r;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return precodatatx_F;
|
|
// normalize
|
|
/* ((int16_t *)precodatatx_F)[0] = (int16_t)((((int16_t *)precodatatx_F)[0]*ONE_OVER_SQRT2_Q15)>>15);
|
|
((int16_t *)precodatatx_F)[1] = (int16_t)((((int16_t *)precodatatx_F)[1]*ONE_OVER_SQRT2_Q15)>>15);*/
|
|
}
|
|
|
|
c16_t nr_layer_precoder_cm(int n_layers,
|
|
int n_symbols,
|
|
int symSz,
|
|
c16_t datatx_F_precoding[n_layers][n_symbols][symSz],
|
|
int ap,
|
|
nfapi_nr_pm_pdu_t *pmi_pdu,
|
|
int symbol,
|
|
int offset)
|
|
{
|
|
c16_t precodatatx_F = {0};
|
|
for (int al = 0; al < n_layers; al++) {
|
|
nfapi_nr_pm_weights_t *w = &pmi_pdu->weights[al][ap];
|
|
c16_t prec_weight = {.r = w->precoder_weight_Re, .i = w->precoder_weight_Im};
|
|
precodatatx_F = c16maddShift(datatx_F_precoding[al][symbol][offset], prec_weight, precodatatx_F, 15);
|
|
}
|
|
return precodatatx_F;
|
|
}
|
|
|
|
void nr_layer_precoder_simd(const int n_layers,
|
|
const int n_symbols,
|
|
const int symSz,
|
|
const c16_t txdataF_res_mapped[n_layers][n_symbols][symSz],
|
|
const int ant,
|
|
const nfapi_nr_pm_pdu_t *pmi_pdu,
|
|
const int symbol,
|
|
const int sc_offset,
|
|
const int re_cnt,
|
|
c16_t *txdataF_precoded)
|
|
{
|
|
uint32_t sc = sc_offset;
|
|
c16_t prec_weight = {0};
|
|
// For x86, use 256 SIMD for every 8 RE and 128 SIMD for last 4 RE
|
|
// For aarch64, use 128 SIMD for every 4 RE
|
|
|
|
// 256 SIMD: Do 8 RE in one iteration, 3 iterations for 2 RB
|
|
#ifdef __AVX2__
|
|
const uint32_t re_cnt_align8 = re_cnt & ~7;
|
|
for(; sc < sc_offset + (re_cnt_align8); sc += sizeof(simde__m256i) / sizeof(prec_weight)) {
|
|
// Matrix multiplication for 4 elements of the result (sizeof(simde__m256i) / sizeof(*prec_matrix) = 8)
|
|
simde__m256i y = simde_mm256_set1_epi16(0); // Y = W[0]*X[0] + W[1]*X[1] + ... + W[nrOfLayers-1]*X[nrOfLayers-1]
|
|
for(int nl = 0; nl < n_layers; nl++) {
|
|
prec_weight.r = pmi_pdu->weights[nl][ant].precoder_weight_Re;
|
|
prec_weight.i = pmi_pdu->weights[nl][ant].precoder_weight_Im;
|
|
|
|
const simde__m256i x = simde_mm256_loadu_epi32(&txdataF_res_mapped[nl][symbol][sc]);
|
|
|
|
// Rearrange precoding matrix weight to match complex multiplication and broadcast it to match SIMD size
|
|
const simde__m256i w_c = simde_mm256_set1_epi32(c16toI32(c16conj(prec_weight))); // broadcast conjugate of w
|
|
const simde__m256i w_s = simde_mm256_set1_epi32(c16toI32(c16swap(prec_weight))); // broadcast swapped real and img of w
|
|
|
|
// Multiplication and shift
|
|
const simde__m256i reals = simde_mm256_srai_epi32(simde_mm256_madd_epi16(x, w_c), 15); // (int32_t) .r = (x.r * w.r - x.i * w.i) >> 15
|
|
const simde__m256i imags = simde_mm256_slli_epi32(simde_mm256_madd_epi16(x, w_s), 1); // (int32_t) .i = (x.r * w.i + x.i * w.r) << 1, since higher 16 bit of each 32 bit is taken by blend_epi16
|
|
|
|
// Re-arrange to match c16_t format
|
|
const simde__m256i produ = simde_mm256_blend_epi16(reals, imags, 0xAA);
|
|
|
|
// Accumulate the product
|
|
y = simde_mm256_adds_epi16(y, produ);
|
|
}
|
|
// Store the result to txdataF
|
|
simde_mm256_storeu_si256(&txdataF_precoded[sc], y);
|
|
}
|
|
#endif
|
|
|
|
// 128 SIMD: Do 4 RE in one iteration, 3 iterations for 1 RB
|
|
const uint32_t re_cnt_align4 = re_cnt & ~3;
|
|
for(; sc < sc_offset+re_cnt_align4; sc += sizeof(simde__m128i) / sizeof(prec_weight)) {
|
|
#ifdef DEBUG_DLSCH_PRECODING_PRINT_WITH_TRIVIAL // Get result with trivial solution, TODO: To be removed
|
|
c16_t y_triv[4];
|
|
for(int i = 0; i < 4; i++)
|
|
y_triv[i] = nr_layer_precoder_cm(n_layers,
|
|
NR_SYMBOLS_PER_SLOT,
|
|
symSz,
|
|
txdataF_res_mapped,
|
|
ant,
|
|
pmi_pdu,
|
|
symbol,
|
|
sc + i);
|
|
memcpy(&txdataF_precoded[sc], y_triv, sizeof(y_triv));
|
|
#endif
|
|
|
|
// Matrix multiplication for 4 elements of the result (sizeof(simde__m128i) / sizeof(c16_t) = 4)
|
|
simde__m128i y = simde_mm_set1_epi16(0); // Y = W[0]*X[0] + W[1]*X[1] + ... + W[nrOfLayers-1]*X[nrOfLayers-1]
|
|
for(int nl = 0; nl < n_layers; nl++) {
|
|
prec_weight.r = pmi_pdu->weights[nl][ant].precoder_weight_Re;
|
|
prec_weight.i = pmi_pdu->weights[nl][ant].precoder_weight_Im;
|
|
|
|
const simde__m128i x = simde_mm_loadu_epi32(&txdataF_res_mapped[nl][symbol][sc]);
|
|
|
|
// Rearrange precoding matrix weight to match complex multiplication and broadcast it to match SIMD size
|
|
const simde__m128i w_c = simde_mm_set1_epi32(c16toI32(c16conj(prec_weight))); // broadcast conjugate of w
|
|
const simde__m128i w_s = simde_mm_set1_epi32(c16toI32(c16swap(prec_weight))); // broadcast swapped real and img of w
|
|
|
|
// Multiplication and shift
|
|
const simde__m128i reals = simde_mm_srai_epi32(simde_mm_madd_epi16(x, w_c), 15); // (int32_t) .r = (x.r * w.r - x.i * w.i) >> 15
|
|
const simde__m128i imags = simde_mm_slli_epi32(simde_mm_madd_epi16(x, w_s), 1); // (int32_t) .i = (x.r * w.i + x.i * w.r) << 1, since higher 16 bit of each 32 bit is taken by blend_epi16
|
|
|
|
/* Re-arrange to match c16_t format
|
|
bit index: 0 | 16 | 32 | 48 | 64 | 80 | 96 | 112
|
|
reals = {R0.r[15..30] | R0.r[31] (0)*15 | R1.r[15..30] | R1.r[31] (0)*15 | R2.r[15..30] | R2.r[31] (0)*15 | R3.r[15..30] | R3.r[31] (0)*15}
|
|
imags = {0 R0.i[0..14]| R0.i[15..30] | 0 R1.i[0..14]| R1.i[15..30] | 0 R2.i[0..14]| R2.i[15..30] | 0 R3.i[0..14]| R3.i[15..30] }
|
|
16b from {reals | imags | reals | imags | reals | imags | reals | imags }
|
|
produ = {R0.r[15..30] | R0.i[15..30] | R1.r[15..30] | R1.i[15..30] | R2.r[15..30] | R2.i[15..30] | R3.r[15..30] | R3.i[15..30] }
|
|
*/
|
|
const simde__m128i produ = simde_mm_blend_epi16(reals, imags, 0xAA);
|
|
|
|
// Accumulate the product
|
|
y = simde_mm_adds_epi16(y, produ);
|
|
}
|
|
// Store the result to txdataF
|
|
simde_mm_storeu_si128(&txdataF_precoded[sc], y);
|
|
|
|
#ifdef DEBUG_DLSCH_PRECODING_PRINT_WITH_TRIVIAL // Print simd and trivial result, TODO: To be removed
|
|
c16_t *y_simd = (c16_t*) &y;
|
|
printf("debug_to_be_removed re_cnt=%d, sc=%u, y_simd=(%+4d,%+4d), (%+4d,%+4d), (%+4d,%+4d), (%+4d,%+4d)\n",
|
|
re_cnt,
|
|
sc,
|
|
y_simd[0].r,
|
|
y_simd[0].i,
|
|
y_simd[1].r,
|
|
y_simd[1].i,
|
|
y_simd[2].r,
|
|
y_simd[2].i,
|
|
y_simd[3].r,
|
|
y_simd[3].i);
|
|
printf("debug_to_be_removed re_cnt=%d, sc=%u, y_triv=(%+4d,%+4d), (%+4d,%+4d), (%+4d,%+4d), (%+4d,%+4d)\n",
|
|
re_cnt,
|
|
sc,
|
|
y_triv[0].r,
|
|
y_triv[0].i,
|
|
y_triv[1].r,
|
|
y_triv[1].i,
|
|
y_triv[2].r,
|
|
y_triv[2].i,
|
|
y_triv[3].r,
|
|
y_triv[3].i);
|
|
#endif
|
|
}
|
|
}
|