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:
Jaroslava Fiedlerova
2025-10-13 11:39:57 +02:00
14 changed files with 422 additions and 227 deletions
+344 -102
View File
@@ -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
+11 -6
View File
@@ -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
*/
+1 -34
View File
@@ -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;
+2
View File
@@ -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;
+9 -1
View File
@@ -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");