Merge remote-tracking branch 'origin/precoder_optimization' into integration_2025_w42 (!3623)
Optimization of PDSCH precoding operation x86/aarch64 optimizations for precoding, more complete timing reporting in nr_dlsim. Timing report in nr_dlsim now shows the total PDSCH generation time and is broken down into Layer mapping, resource mapping and precoding. The optimizations for x86 are basically reorganizing loops (for AVX512,AVX2, NEON but not SSE2) to minimize repetitive instructions when formatting of the precoder weights, and creation of inline functions for complex multiply-add. AVX512 optimization was added. requiring AVX512BW. Detection of up to 4 PRBs with common PMI for efficiency with AVX512 (was 2 in develop for AVX2). For aarch64, native coding of the precoder to avoid inefficient SIMDe translations for this operation. nr_dlsim now uses default precoding indices depending on the scenario instead of 0 which doesn't do precoding at all. See merge request description for nr_dlsim test results.
This commit is contained in:
@@ -295,7 +295,7 @@ void nr_layer_mapping(int nbCodes,
|
||||
c16_t *tx0 = tx_layers[0];
|
||||
c16_t *tx1 = tx_layers[1];
|
||||
c16_t *tx2 = tx_layers[2];
|
||||
#if defined(__AVX512BW__)
|
||||
#if defined(__AVX512F) && defined(__AVX512VBMI__)
|
||||
simde__m512i perm3_0 = simde_mm512_set_epi32(13 + 16,
|
||||
10 + 16,
|
||||
7 + 16,
|
||||
@@ -433,7 +433,7 @@ void nr_layer_mapping(int nbCodes,
|
||||
c16_t *tx1 = tx_layers[1];
|
||||
c16_t *tx2 = tx_layers[2];
|
||||
c16_t *tx3 = tx_layers[3];
|
||||
#if defined(__AVX512BW__)
|
||||
#if defined(__AVX512VBMI__)
|
||||
simde__m512i perm4 = simde_mm512_set_epi32(15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0);
|
||||
for (; i < (n_symbs & ~15); i += 16) {
|
||||
simde__m512i e = simde_mm512_permutexvar_epi32(perm4, *(simde__m512i *)(mod + i));
|
||||
@@ -710,13 +710,104 @@ c16_t nr_layer_precoder_cm(int n_layers,
|
||||
{
|
||||
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};
|
||||
c16_t prec_weight = pmi_pdu->weights[al][ap];
|
||||
precodatatx_F = c16maddShift(datatx_F_precoding[al][offset], prec_weight, precodatatx_F, 15);
|
||||
}
|
||||
return precodatatx_F;
|
||||
}
|
||||
|
||||
#if defined(__AVX512F__) && defined(__AVX512BW__)
|
||||
|
||||
static inline __attribute__((always_inline)) __m512i cmac0_prec512(__m512i x, __m512i w_c, __m512i w_s) {
|
||||
|
||||
// Multiplication and shift
|
||||
const __m512i reals =
|
||||
_mm512_srai_epi32(_mm512_madd_epi16(x, w_c), 15); // (int32_t) .r = (x.r * w.r - x.i * w.i) >> 15
|
||||
const __m512i imags =
|
||||
_mm512_slli_epi32(_mm512_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
|
||||
return _mm512_mask_blend_epi16(0xAAAAAAAA,reals, imags);
|
||||
|
||||
}
|
||||
static inline __attribute__((always_inline)) __m512i cmac_prec512(__m512i y, __m512i x, __m512i w_c, __m512i w_s) {
|
||||
const __m512i produ = cmac0_prec512(x, w_c, w_s);
|
||||
// Accumulate the product
|
||||
return _mm512_adds_epi16(y, produ);
|
||||
}
|
||||
#endif
|
||||
#ifdef __AVX2__
|
||||
static inline __attribute__((always_inline)) __m256i cmac0_prec256(__m256i x, __m256i w_c, __m256i w_s) {
|
||||
|
||||
// Multiplication and shift
|
||||
const __m256i reals =
|
||||
_mm256_srai_epi32(_mm256_madd_epi16(x, w_c), 15); // (int32_t) .r = (x.r * w.r - x.i * w.i) >> 15
|
||||
const __m256i imags =
|
||||
_mm256_slli_epi32(_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
|
||||
return _mm256_blend_epi16(reals, imags,0xAA);
|
||||
|
||||
}
|
||||
static inline __attribute__((always_inline)) __m256i cmac_prec256(__m256i y, __m256i x, __m256i w_c, __m256i w_s) {
|
||||
const __m256i produ = cmac0_prec256(x, w_c, w_s);
|
||||
// Accumulate the product
|
||||
return _mm256_adds_epi16(y, produ);
|
||||
}
|
||||
#endif
|
||||
#ifdef __aarch64__
|
||||
static inline __attribute__((always_inline)) int16x8_t cmac0_prec128(int16x8_t x, int16x8_t wr, int16x8_t wi) {
|
||||
//
|
||||
int16x8_t xr = vuzp1q_s16(x, x); // even lanes
|
||||
int16x8_t xi = vuzp2q_s16(x, x); // odd lanes
|
||||
// real = ar*br - ai*bi (Q15 scaling via high-half doubling muls)
|
||||
int16x8_t real = vqdmulhq_s16(xr, wr); // ≈ round((2*xr*wr)/2^16)
|
||||
real = vqrdmlshq_s16(real, xi, wi); // real -= round((2*xi*wi)/2^16)
|
||||
//
|
||||
// imag = ar*bi + ai*br
|
||||
int16x8_t imag = vqdmulhq_s16(xr, wi);
|
||||
imag = vqrdmlahq_s16(imag, xi, wr); // imag += round((2*xi*wr)/2^16)
|
||||
//
|
||||
// Re-interleave [real, imag]
|
||||
int16x8x2_t produ = vzipq_s16(real, imag);
|
||||
return produ.val[0];
|
||||
}
|
||||
static inline __attribute__((always_inline)) int16x8_t cmac_prec128(int16x8_t y, int16x8_t x, int16x8_t wr, int16x8_t wi) {
|
||||
int16x8_t produ = cmac0_prec128(x, wr, wi);
|
||||
return vaddq_s16(y, produ);
|
||||
}
|
||||
#else
|
||||
static inline __attribute__((always_inline)) simde__m128i cmac0_prec128(simde__m128i x, simde__m128i w_c, simde__m128i w_s)
|
||||
{
|
||||
// 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] }
|
||||
*/
|
||||
return simde_mm_blend_epi16(reals, imags, 0xAA);
|
||||
}
|
||||
static inline __attribute__((always_inline)) __m128i cmac_prec128(__m128i y, __m128i x, __m128i w_c, __m128i w_s)
|
||||
{
|
||||
const __m128i produ = cmac0_prec128(x, w_c, w_s);
|
||||
// Accumulate the product
|
||||
return simde_mm_adds_epi16(y, produ);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define load_consts(Type, Instruct, Rank) \
|
||||
const Type w_c##Rank = Instruct(c16toI32(c16conj(pmi_pdu->weights[Rank][ant]))); \
|
||||
const Type w_s##Rank = Instruct(c16toI32(c16swap(pmi_pdu->weights[Rank][ant]))); \
|
||||
const Type *in##Rank = (Type *)(txdataF_res_mapped[Rank] + sc_offset + (out-beginning));
|
||||
|
||||
void nr_layer_precoder_simd(const int n_layers,
|
||||
const int symSz,
|
||||
const c16_t txdataF_res_mapped[n_layers][symSz],
|
||||
@@ -726,113 +817,264 @@ void nr_layer_precoder_simd(const int n_layers,
|
||||
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
|
||||
AssertFatal(n_layers > 0 && n_layers <= 4, "Shouldn't get here, n_layers %d\n", n_layers);
|
||||
|
||||
// 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_si256(&txdataF_res_mapped[nl][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);
|
||||
// 512/256 SIMD: Do 16/8 RE in one iteration, 3 iterations for 2 RB
|
||||
c16_t *beginning = txdataF_precoded + sc_offset;
|
||||
c16_t *out=beginning;
|
||||
#if defined(__AVX512F__) && defined(__AVX512BW__)
|
||||
{
|
||||
c16_t *end = out + (re_cnt & ~15);
|
||||
load_consts(__m512i, _mm512_set1_epi32, 0);
|
||||
if (n_layers == 1) {
|
||||
for (; out < end; out += sizeof(__m512i) / sizeof(*out)) {
|
||||
const __m512i x = _mm512_loadu_si512(in0++);
|
||||
// Matrix multiplication for 4 elements of the result (sizeof(simde__m256i) / sizeof(*prec_matrix) = 8)
|
||||
__m512i y = cmac0_prec512(x, w_c0, w_s0);
|
||||
_mm512_storeu_si512(out, y);
|
||||
}
|
||||
} else if (n_layers == 2) {
|
||||
load_consts(__m512i, _mm512_set1_epi32, 1);
|
||||
for (; out < end; out += sizeof(__m512i) / sizeof(*out)) {
|
||||
const __m512i x = _mm512_loadu_si512(in0++);
|
||||
const __m512i x1 = _mm512_loadu_si512(in1++);
|
||||
// Matrix multiplication for 4 elements of the result (sizeof(simde__m256i) / sizeof(*prec_matrix) = 8)
|
||||
__m512i y = cmac0_prec512(x, w_c0, w_s0);
|
||||
y = cmac_prec512(y, x1, w_c1, w_s1);
|
||||
_mm512_storeu_si512(out, y);
|
||||
}
|
||||
} else if (n_layers == 3) {
|
||||
load_consts(__m512i, _mm512_set1_epi32, 1);
|
||||
load_consts(__m512i, _mm512_set1_epi32, 2);
|
||||
for (; out < end; out += sizeof(__m512i) / sizeof(*out)) {
|
||||
const __m512i x = _mm512_loadu_si512(in0++);
|
||||
const __m512i x1 = _mm512_loadu_si512(in1++);
|
||||
const __m512i x2 = _mm512_loadu_si512(in2++);
|
||||
// Matrix multiplication for 4 elements of the result (sizeof(simde__m256i) / sizeof(*prec_matrix) = 8)
|
||||
__m512i y = cmac0_prec512(x, w_c0, w_s0);
|
||||
y = cmac_prec512(y, x1, w_c1, w_s1);
|
||||
y = cmac_prec512(y, x2, w_c2, w_s2);
|
||||
_mm512_storeu_si512(out, y);
|
||||
}
|
||||
} else if (n_layers == 4) {
|
||||
load_consts(__m512i, _mm512_set1_epi32, 1);
|
||||
load_consts(__m512i, _mm512_set1_epi32, 2);
|
||||
load_consts(__m512i, _mm512_set1_epi32, 3);
|
||||
for (; out < end; out += sizeof(__m512i) / sizeof(*out)) {
|
||||
const __m512i x = _mm512_loadu_si512(in0++);
|
||||
const __m512i x1 = _mm512_loadu_si512(in1++);
|
||||
const __m512i x2 = _mm512_loadu_si512(in2++);
|
||||
const __m512i x3 = _mm512_loadu_si512(in3++);
|
||||
// Matrix multiplication for 4 elements of the result (sizeof(simde__m256i) / sizeof(*prec_matrix) = 8)
|
||||
__m512i y = cmac0_prec512(x, w_c0, w_s0);
|
||||
y = cmac_prec512(y, x1, w_c1, w_s1);
|
||||
y = cmac_prec512(y, x2, w_c2, w_s2);
|
||||
y = cmac_prec512(y, x3, w_c3, w_s3);
|
||||
_mm512_storeu_si512(out, y);
|
||||
}
|
||||
}
|
||||
// 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 __AVX2__
|
||||
{
|
||||
c16_t *end = beginning + (re_cnt & ~7);
|
||||
load_consts(simde__m256i, simde_mm256_set1_epi32, 0);
|
||||
if (n_layers == 1) {
|
||||
for (; out < end; out += sizeof(simde__m256i) / sizeof(*out)) {
|
||||
const simde__m256i x0 = simde_mm256_loadu_si256(in0++);
|
||||
// Accumulate the product
|
||||
simde__m256i y = cmac0_prec256(x0, w_c0, w_s0);
|
||||
// Store the result to txdataF
|
||||
simde_mm256_storeu_si256(out, y);
|
||||
}
|
||||
} else if (n_layers == 2) {
|
||||
load_consts(simde__m256i, simde_mm256_set1_epi32, 1);
|
||||
for (; out < end; out += sizeof(simde__m256i) / sizeof(*out)) {
|
||||
const simde__m256i x0 = simde_mm256_loadu_si256(in0++);
|
||||
const simde__m256i x1 = simde_mm256_loadu_si256(in1++);
|
||||
// Accumulate the product
|
||||
simde__m256i y = cmac0_prec256(x0, w_c0, w_s0);
|
||||
y = cmac_prec256(y, x1, w_c1, w_s1);
|
||||
// Store the result to txdataF
|
||||
simde_mm256_storeu_si256(out, y);
|
||||
}
|
||||
} else if (n_layers == 3) {
|
||||
load_consts(simde__m256i, simde_mm256_set1_epi32, 1);
|
||||
load_consts(simde__m256i, simde_mm256_set1_epi32, 2);
|
||||
for (; out < end; out += sizeof(simde__m256i) / sizeof(*out)) {
|
||||
const simde__m256i x0 = simde_mm256_loadu_si256(in0++);
|
||||
const simde__m256i x1 = simde_mm256_loadu_si256(in1++);
|
||||
const simde__m256i x2 = simde_mm256_loadu_si256(in2++);
|
||||
simde__m256i y = cmac0_prec256(x0, w_c0, w_s0);
|
||||
y = cmac_prec256(y, x1, w_c1, w_s1);
|
||||
y = cmac_prec256(y, x2, w_c2, w_s2);
|
||||
// Store the result to txdataF
|
||||
simde_mm256_storeu_si256(out, y);
|
||||
}
|
||||
} else if (n_layers == 4) {
|
||||
load_consts(simde__m256i, simde_mm256_set1_epi32, 1);
|
||||
load_consts(simde__m256i, simde_mm256_set1_epi32, 2);
|
||||
load_consts(simde__m256i, simde_mm256_set1_epi32, 3);
|
||||
for (; out < end; out += sizeof(simde__m256i) / sizeof(*out)) {
|
||||
const simde__m256i x0 = simde_mm256_loadu_si256(in0++);
|
||||
const simde__m256i x1 = simde_mm256_loadu_si256(in1++);
|
||||
const simde__m256i x2 = simde_mm256_loadu_si256(in2++);
|
||||
const simde__m256i x3 = simde_mm256_loadu_si256(in3++);
|
||||
simde__m256i y = cmac0_prec256(x0, w_c0, w_s0);
|
||||
y = cmac_prec256(y, x1, w_c1, w_s1);
|
||||
y = cmac_prec256(y, x2, w_c2, w_s2);
|
||||
y = cmac_prec256(y, x3, w_c3, w_s3);
|
||||
// Store the result to txdataF
|
||||
simde_mm256_storeu_si256(out, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
c16_t *end = beginning + (re_cnt & ~3);
|
||||
#ifdef DEBUG_DLSCH_PRECODING_PRINT_WITH_TRIVIAL // Get result with trivial solution, TODO: To be removed
|
||||
// 128 SIMD: Do 4 RE in one iteration, 3 iterations for 1 RB
|
||||
for (; out < end; out += sizeof(simde__m128i) / sizeof(*out)) {
|
||||
c16_t y_triv[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
y_triv[i] = nr_layer_precoder_cm(n_layers, symSz, txdataF_res_mapped, ant, pmi_pdu, 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_si128(&txdataF_res_mapped[nl][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
|
||||
memcpy(out, y_triv, sizeof(y_triv));
|
||||
}
|
||||
#endif
|
||||
#ifdef __aarch64__
|
||||
load_consts(int16x8_t, vdupq_n_s16, 0);
|
||||
if (n_layers == 1) {
|
||||
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
|
||||
const int16x8_t x0 = vld1q_s16((const int16_t *)in0++);
|
||||
// Accumulate the product
|
||||
int16x8_t y = cmac0_prec128(x0, w_c0, w_s0);
|
||||
// Store the result to txdataF
|
||||
*(int16x8_t *)out = y;
|
||||
}
|
||||
}
|
||||
if (n_layers == 2) {
|
||||
load_consts(int16x8_t, vdupq_n_s16, 1);
|
||||
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
|
||||
const int16x8_t x0 = vld1q_s16((const int16_t *)in0++);
|
||||
const int16x8_t x1 = vld1q_s16((const int16_t *)in1++);
|
||||
// Accumulate the product
|
||||
int16x8_t y = cmac0_prec128(x0, w_c0, w_s0);
|
||||
y = cmac_prec128(y, x1, w_c1, w_s1);
|
||||
// Store the result to txdataF
|
||||
*(int16x8_t *)out = y;
|
||||
}
|
||||
}
|
||||
if (n_layers == 3) {
|
||||
load_consts(int16x8_t, vdupq_n_s16, 1);
|
||||
load_consts(int16x8_t, vdupq_n_s16, 2);
|
||||
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
|
||||
const int16x8_t x0 = vld1q_s16((const int16_t *)in0++);
|
||||
const int16x8_t x1 = vld1q_s16((const int16_t *)in1++);
|
||||
const int16x8_t x2 = vld1q_s16((const int16_t *)in2++);
|
||||
// Accumulate the product
|
||||
int16x8_t y = cmac0_prec128(x0, w_c0, w_s0);
|
||||
;
|
||||
y = cmac_prec128(y, x1, w_c1, w_s1);
|
||||
y = cmac_prec128(y, x2, w_c2, w_s2);
|
||||
// Store the result to txdataF
|
||||
*(int16x8_t *)out = y;
|
||||
}
|
||||
}
|
||||
if (n_layers == 4) {
|
||||
load_consts(int16x8_t, vdupq_n_s16, 1);
|
||||
load_consts(int16x8_t, vdupq_n_s16, 2);
|
||||
load_consts(int16x8_t, vdupq_n_s16, 3);
|
||||
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
|
||||
const int16x8_t x0 = vld1q_s16((const int16_t *)in0++);
|
||||
const int16x8_t x1 = vld1q_s16((const int16_t *)in1++);
|
||||
const int16x8_t x2 = vld1q_s16((const int16_t *)in2++);
|
||||
const int16x8_t x3 = vld1q_s16((const int16_t *)in3++);
|
||||
// Accumulate the product
|
||||
int16x8_t y = cmac0_prec128(x0, w_c0, w_s0);
|
||||
;
|
||||
y = cmac_prec128(y, x1, w_c1, w_s1);
|
||||
y = cmac_prec128(y, x2, w_c2, w_s2);
|
||||
y = cmac_prec128(y, x3, w_c3, w_s3);
|
||||
// Store the result to txdataF
|
||||
*(int16x8_t *)out = y;
|
||||
}
|
||||
}
|
||||
#else
|
||||
load_consts(simde__m128i, simde_mm_set1_epi32, 0);
|
||||
if (n_layers == 1) {
|
||||
for (; out < end; out += sizeof(simde__m128i) / sizeof(*out)) {
|
||||
const simde__m128i x0 = simde_mm_loadu_si128(in0++);
|
||||
// Accumulate the product
|
||||
simde__m128i y = cmac0_prec128(x0, w_c0, w_s0);
|
||||
// Store the result to txdataF
|
||||
simde_mm_storeu_si128(out, y);
|
||||
}
|
||||
} else if (n_layers == 2) {
|
||||
load_consts(simde__m128i, simde_mm_set1_epi32, 1);
|
||||
for (; out < end; out += sizeof(simde__m128i) / sizeof(*out)) {
|
||||
const simde__m128i x0 = simde_mm_loadu_si128(in0++);
|
||||
const simde__m128i x1 = simde_mm_loadu_si128(in1++);
|
||||
// Accumulate the product
|
||||
simde__m128i y = cmac0_prec128(x0, w_c0, w_s0);
|
||||
y = cmac_prec128(y, x1, w_c1, w_s1);
|
||||
// Store the result to txdataF
|
||||
simde_mm_storeu_si128(out, y);
|
||||
}
|
||||
} else if (n_layers == 3) {
|
||||
load_consts(simde__m128i, simde_mm_set1_epi32, 1);
|
||||
load_consts(simde__m128i, simde_mm_set1_epi32, 2);
|
||||
for (; out < end; out += sizeof(simde__m128i) / sizeof(*out)) {
|
||||
const simde__m128i x0 = simde_mm_loadu_si128(in0++);
|
||||
const simde__m128i x1 = simde_mm_loadu_si128(in1++);
|
||||
const simde__m128i x2 = simde_mm_loadu_si128(in2++);
|
||||
simde__m128i y = cmac0_prec128(x0, w_c0, w_s0);
|
||||
y = cmac_prec128(y, x1, w_c1, w_s1);
|
||||
y = cmac_prec128(y, x2, w_c2, w_s2);
|
||||
// Store the result to txdataF
|
||||
simde_mm_storeu_si128(out, y);
|
||||
}
|
||||
} else if (n_layers == 4) {
|
||||
load_consts(simde__m128i, simde_mm_set1_epi32, 1);
|
||||
load_consts(simde__m128i, simde_mm_set1_epi32, 2);
|
||||
load_consts(simde__m128i, simde_mm_set1_epi32, 3);
|
||||
for (; out < end; out += sizeof(simde__m128i) / sizeof(*out)) {
|
||||
const simde__m128i x0 = simde_mm_loadu_si128(in0++);
|
||||
const simde__m128i x1 = simde_mm_loadu_si128(in1++);
|
||||
const simde__m128i x2 = simde_mm_loadu_si128(in2++);
|
||||
const simde__m128i x3 = simde_mm_loadu_si128(in3++);
|
||||
simde__m128i y = cmac0_prec128(x0, w_c0, w_s0);
|
||||
y = cmac_prec128(y, x1, w_c1, w_s1);
|
||||
y = cmac_prec128(y, x2, w_c2, w_s2);
|
||||
y = cmac_prec128(y, x3, w_c3, w_s3);
|
||||
// Store the result to txdataF
|
||||
simde_mm_storeu_si128(out, y);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#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
|
||||
}
|
||||
|
||||
@@ -85,8 +85,7 @@ TEST(NrLayerPrecoderTest, Basic)
|
||||
// Create and populate the pmi_pdu structure
|
||||
nfapi_nr_pm_pdu_t pmi_pdu = {0};
|
||||
for (int layer = 0; layer < n_layers; ++layer) {
|
||||
pmi_pdu.weights[layer][ant].precoder_weight_Re = -SHRT_MAX; // Q15 representation of (~-1.0) -0.999969482421875 (-1+2^-15)
|
||||
pmi_pdu.weights[layer][ant].precoder_weight_Im = 0;
|
||||
pmi_pdu.weights[layer][ant] = (c16_t){-SHRT_MAX, 0};
|
||||
}
|
||||
|
||||
// Call the C function
|
||||
@@ -131,8 +130,7 @@ TEST(NrLayerPrecoderTest, SIMD)
|
||||
// Create and populate the pmi_pdu structure
|
||||
nfapi_nr_pm_pdu_t pmi_pdu = {0};
|
||||
for (int layer = 0; layer < n_layers; ++layer) {
|
||||
pmi_pdu.weights[layer][ant].precoder_weight_Re = -SHRT_MAX; // Q15 representation of (~-1.0) -0.999969482421875 (-1+2^-15)
|
||||
pmi_pdu.weights[layer][ant].precoder_weight_Im = 0;
|
||||
pmi_pdu.weights[layer][ant] = (c16_t){-SHRT_MAX, 0};
|
||||
}
|
||||
|
||||
// Call the C function
|
||||
@@ -182,10 +180,8 @@ TEST(NrLayerPrecoderTest, Compare_CM_SIMD)
|
||||
// Could not use convert_precoder_weight() as complex.h could not be used in googletest
|
||||
// Use the logic in convert_precoder_weight()
|
||||
// precoder [−1,−j]
|
||||
pmi_pdu.weights[layer][0].precoder_weight_Re = -SHRT_MAX; // Q15 representation of (~-1.0) -0.999969482421875 (-1+2^-15)
|
||||
pmi_pdu.weights[layer][0].precoder_weight_Im = 0x0000; // Q15 representation of 0
|
||||
pmi_pdu.weights[layer][1].precoder_weight_Re = 0x0000; // Q15 representation of 0
|
||||
pmi_pdu.weights[layer][1].precoder_weight_Im = -SHRT_MAX; // Q15 representation of (~-1.0) -0.999969482421875 (-1+2^-15)
|
||||
pmi_pdu.weights[layer][0] = (c16_t){-SHRT_MAX, 0};
|
||||
pmi_pdu.weights[layer][1] = (c16_t){0, -SHRT_MAX};
|
||||
}
|
||||
|
||||
// Get the results for all the antenna
|
||||
|
||||
@@ -470,12 +470,14 @@ static inline void do_txdataF(c16_t **txdataF,
|
||||
// get pmi info
|
||||
const int pmi = (pb->prg_size > 0) ? (pb->prgs_list[(int)rb / pb->prg_size].pm_idx) : 0;
|
||||
const int pmi2 = (rb < (rel15->rbSize - 1) && pb->prg_size > 0) ? (pb->prgs_list[(int)(rb + 1) / pb->prg_size].pm_idx) : -1;
|
||||
const int pmi3 = (rb < (rel15->rbSize - 2) && pb->prg_size > 0) ? (pb->prgs_list[(int)(rb + 2) / pb->prg_size].pm_idx) : -1;
|
||||
const int pmi4 = (rb < (rel15->rbSize - 3) && pb->prg_size > 0) ? (pb->prgs_list[(int)(rb + 3) / pb->prg_size].pm_idx) : -1;
|
||||
|
||||
// If pmi of next RB and pmi of current RB are the same, we do 2 RB in a row
|
||||
// if pmi differs, or current rb is the end (rel15->rbSize - 1), than we do 1 RB in a row
|
||||
const int rb_step = pmi == pmi2 ? 2 : 1;
|
||||
int rb_step0 = pmi == pmi2 ? 2 : 1;
|
||||
const int rb_step = rb_step0==2 && pmi3==pmi && pmi4==pmi ? 4 : rb_step0;
|
||||
const int re_cnt = NR_NB_SC_PER_RB * rb_step;
|
||||
|
||||
if (pmi == 0) { // unitary Precoding
|
||||
if (subCarrier + re_cnt <= symbol_sz) { // RB does not cross DC
|
||||
if (ant < rel15->nrOfLayers)
|
||||
@@ -643,6 +645,7 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC
|
||||
#endif
|
||||
}
|
||||
|
||||
start_meas(&gNB->dlsch_pdsch_generation_stats);
|
||||
/// Resource mapping
|
||||
// Non interleaved VRB to PRB mapping
|
||||
uint16_t start_sc = frame_parms->first_carrier_offset + (rel15->rbStart + rel15->BWPStart) * NR_NB_SC_PER_RB;
|
||||
@@ -664,6 +667,7 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC
|
||||
c16_t mod_dmrs[(n_dmrs+63)&~63] __attribute__((aligned(64)));
|
||||
unsigned int re_beginning_of_symbol = 0;
|
||||
|
||||
start_meas(&gNB->dlsch_layer_mapping_stats);
|
||||
int layerSz2 = (layerSz + 63) & ~63;
|
||||
c16_t tx_layers[rel15->nrOfLayers][layerSz2] __attribute__((aligned(64)));
|
||||
memset(tx_layers, 0, sizeof(tx_layers));
|
||||
@@ -676,7 +680,6 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC
|
||||
// pmi = prgs_list[rbidx/prg_size].pm_idx, rbidx =0,...,rbSize-1
|
||||
// The Precoding matrix:
|
||||
// The Codebook Type I
|
||||
start_meas(&gNB->dlsch_precoding_stats);
|
||||
nfapi_nr_tx_precoding_and_beamforming_t *pb = &rel15->precodingAndBeamforming;
|
||||
// beam number in multi-beam scenario (concurrent beams)
|
||||
int bitmap = SL_to_bitmap(rel15->StartSymbolIndex, rel15->NrOfSymbols);
|
||||
@@ -689,9 +692,10 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC
|
||||
bitmap);
|
||||
|
||||
c16_t **txdataF = gNB->common_vars.txdataF[beam_nb];
|
||||
|
||||
stop_meas(&gNB->dlsch_layer_mapping_stats);
|
||||
// Loop Over OFDM symbols:
|
||||
for (int l_symbol = rel15->StartSymbolIndex; l_symbol < rel15->StartSymbolIndex + rel15->NrOfSymbols; l_symbol++) {
|
||||
start_meas(&gNB->dlsch_resource_mapping_stats);
|
||||
int l_prime = 0; // single symbol layer 0
|
||||
int l_overline = get_l0(rel15->dlDmrsSymbPos);
|
||||
|
||||
@@ -758,13 +762,14 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC
|
||||
re_beginning_of_symbol += layer_sz;
|
||||
stop_meas(&gNB->dlsch_resource_mapping_stats);
|
||||
|
||||
start_meas(&gNB->dlsch_precoding_stats);
|
||||
for (int ant = 0; ant < frame_parms->nb_antennas_tx; ant++) {
|
||||
const size_t txdataF_offset_per_symbol = l_symbol * symbol_sz + txdataF_offset;
|
||||
do_txdataF(txdataF, symbol_sz, txdataF_precoding, gNB, rel15, ant, start_sc, txdataF_offset_per_symbol);
|
||||
}
|
||||
stop_meas(&gNB->dlsch_precoding_stats);
|
||||
}
|
||||
|
||||
stop_meas(&gNB->dlsch_precoding_stats);
|
||||
stop_meas(&gNB->dlsch_pdsch_generation_stats);
|
||||
/* output and its parts for each dlsch should be aligned on 64 bytes (or 8 * 64 bits)
|
||||
* should remain a multiple of 8 * 64 with enough offset to fit each dlsch
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/platform_types.h"
|
||||
#include "PHY/sse_intrin.h"
|
||||
|
||||
#include "common/utils/assertions.h"
|
||||
@@ -51,39 +51,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ALIGNARRAYSIZE(a, b) (((a + b - 1) / b) * b)
|
||||
#define ALNARS_16_4(a) ALIGNARRAYSIZE(a, 4)
|
||||
|
||||
typedef struct complexd {
|
||||
double r;
|
||||
double i;
|
||||
} cd_t;
|
||||
|
||||
typedef struct complexf {
|
||||
float r;
|
||||
float i;
|
||||
} cf_t;
|
||||
|
||||
typedef struct complex8 {
|
||||
int8_t r;
|
||||
int8_t i;
|
||||
} c8_t;
|
||||
|
||||
typedef struct complex16 {
|
||||
int16_t r;
|
||||
int16_t i;
|
||||
} c16_t;
|
||||
|
||||
typedef struct complex32 {
|
||||
int32_t r;
|
||||
int32_t i;
|
||||
} c32_t;
|
||||
|
||||
typedef struct complex64 {
|
||||
int64_t r;
|
||||
int64_t i;
|
||||
} c64_t;
|
||||
|
||||
typedef struct {
|
||||
int dim1;
|
||||
int dim2;
|
||||
|
||||
@@ -501,6 +501,8 @@ typedef struct PHY_VARS_gNB_s {
|
||||
time_stats_t dlsch_encoding_stats;
|
||||
time_stats_t dlsch_modulation_stats;
|
||||
time_stats_t dlsch_scrambling_stats;
|
||||
time_stats_t dlsch_pdsch_generation_stats;
|
||||
time_stats_t dlsch_layer_mapping_stats;
|
||||
time_stats_t dlsch_resource_mapping_stats;
|
||||
time_stats_t dlsch_precoding_stats;
|
||||
time_stats_t tinput;
|
||||
|
||||
@@ -1029,7 +1029,10 @@ int main(int argc, char **argv)
|
||||
reset_meas(&gNB->dlsch_rate_matching_stats);
|
||||
reset_meas(&gNB->dlsch_segmentation_stats);
|
||||
reset_meas(&gNB->dlsch_modulation_stats);
|
||||
reset_meas(&gNB->dlsch_pdsch_generation_stats);
|
||||
reset_meas(&gNB->dlsch_precoding_stats);
|
||||
reset_meas(&gNB->dlsch_layer_mapping_stats);
|
||||
reset_meas(&gNB->dlsch_resource_mapping_stats);
|
||||
reset_meas(&gNB->dlsch_encoding_stats);
|
||||
reset_meas(&gNB->dci_generation_stats);
|
||||
reset_meas(&gNB->tinput);
|
||||
@@ -1343,7 +1346,12 @@ int main(int argc, char **argv)
|
||||
printStatIndent3(&gNB->dlsch_interleaving_stats, "DLSCH Interleaving time");
|
||||
printStatIndent2(&gNB->dlsch_modulation_stats,"DLSCH modulation time");
|
||||
printStatIndent2(&gNB->dlsch_scrambling_stats, "DLSCH scrambling time");
|
||||
printStatIndent2(&gNB->dlsch_precoding_stats,"DLSCH Mapping/Precoding time");
|
||||
printStatIndent2(&gNB->dlsch_pdsch_generation_stats,"DLSCH PDSCH Generation time");
|
||||
printStatIndent3(&gNB->dlsch_layer_mapping_stats,"DLSCH Layer Mapping time");
|
||||
gNB->dlsch_resource_mapping_stats.trials = gNB->dlsch_layer_mapping_stats.trials;
|
||||
printStatIndent3(&gNB->dlsch_resource_mapping_stats,"DLSCH Resource Mapping time");
|
||||
gNB->dlsch_precoding_stats.trials = gNB->dlsch_layer_mapping_stats.trials;
|
||||
printStatIndent3(&gNB->dlsch_precoding_stats,"DLSCH Precoding time");
|
||||
if (gNB->phase_comp)
|
||||
printStatIndent2(&gNB->phase_comp_stats, "Phase Compensation");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user