Compare commits

...

6 Commits

Author SHA1 Message Date
Raymond Knopp
3b7b75a8e2 CMake: enable +crypto for Cortex-A72 to get hardware PMULL for CRC
Without +crypto, GCC does not define __ARM_FEATURE_AES, so SIMDe's
simde_mm_clmulepi64_si128 falls back to a scalar carry-less multiply
(16 integer multiplies + many AND/XOR ops per call) instead of emitting
vmull_p64.  The NXP LS2160A A72 has full hardware crypto (aes, pmull,
sha1, sha2) but -march=armv8-a+simd does not enable it.

Split the A72 / A53 march cases:
  A72 (0xd08): -march=armv8-a+simd+crypto  (hardware crypto confirmed)
  A53 (0xd03): -march=armv8-a+simd         (crypto optional on A53)

Result on A72 @ 2 GHz (nr_dlsim -n100 -e25 -s25 -R273 -b273 -P -x2 -y4 -z4 -p55):
  DLSCH Outer CRC:    ~250 us -> 22.9 us  (~11x)
  DLSCH segmentation: ~250 us -> 37.5 us  (~6.7x)
  Total DLSCH encoding: ~1500 us -> ~1057 us

Assisted by Claude Sonnet 4.6
2026-06-07 23:21:51 +02:00
Raymond Knopp
2f2a3c1f71 Finishing off the "alignr" additions for all BG1 sizes. Some additional
timing information in gNB TX (overall CRC)

Signed-off-by: Raymond Knopp <raymond.knopp@openairinterface.org>
2026-06-07 22:15:21 +02:00
Raymond Knopp
ee22b6e83a added missing BG1 LDPC encoder for 128-bit with "alignr"
Signed-off-by: Raymond Knopp <raymond.knopp@eurecom.fr>
2026-06-07 17:42:35 +00:00
Raymond Knopp
f668d2e527 nrLDPC: AArch64 ns=8 input interleaving fast path with asm-volatile OR-tree
Replace the generic j-loop (7 serial ORRs, 28-cycle critical path, j-loop
overhead) with a fully-unrolled ns=8 specialization for Cortex-A72:

 - Pre-extract 8 segment base pointers into registers so GCC holds them
   across the 528-iteration inner loop instead of reloading from input[].

 - CONTRIB macro: vcombine(vld1_dup(p+bi), vld1_dup(p+bi+1)) + VTST +
   VAND to produce each segment's bit-mask contribution in one expression;
   avoids ANDNOT+CMPEQ+AND (saves one NEON op per segment per iteration).

 - asm volatile barrier 1: "+w" on all 8 contributions forces GCC to
   materialize r0..r7 as physical NEON registers before any ORR is issued.
   Without it GCC left-folds each ORR greedily as soon as one input is
   ready (28-cycle serial chain).  With it the four level-1 ORRs are
   data-independent and A72's 2-wide NEON pipe issues two per cycle.

 - asm volatile barrier 2: "+w" on r01/r23/r45/r67 forces level-1 results
   before level-2.  GCC's instruction scheduler then fills the 4-cycle
   level-2 ORR latency gap with the loop's scalar work (add i_byte, cmp),
   placing those instructions between level-2 and level-3/4 ORRs.  This
   lets A72's out-of-order engine start the next iteration's address chain
   (lsr bi = i_byte>>3) ~4 cycles earlier, hiding load latency for free.

 - ns != 8 fallback: original j-loop preserved for 1..7 segments.

Measured on NXP LS2160A (Cortex-A72, 2.0 GHz), BG1 Zc=384 (-n100 -S8 -s3.0):
  Before: ~13 µs  →  After: ~9-10 µs  (~25% reduction)

Assisted by Claude Sonnet 4.6
2026-06-07 08:28:07 +02:00
Raymond Knopp
3ff24df579 LDPC encoder AArch64 optimizations (BG1 Zc=384).
Input interleaving (ldpc_encoder_optim8segmulti.c): replace the SIMDE
shuffle_epi8 path (-> NEON TBL, 5-cycle latency) with direct NEON
vcombine(vld1_dup_u8, vld1_dup_u8) to broadcast each input byte, and
use VTST+AND instead of ANDNOT+CMPEQ+AND (one fewer op per segment per
iteration).  On NXP LS2160A (Cortex-A72, 2 GHz) this reduces the input
interleaving phase from 20.2 us to ~2.0 us (10x speedup), and total
encoder latency from ~126 us to ~107 us for 8 segments.

Parity-check VLA fix (ldpc_encode_parity_check.c): under USE_ALIGNR
(aarch64, BG1, Zc=384) the simd_size memcpy copies are skipped, so
allocate only one copy (vla_simd=1) instead of simd_size=32, reducing
the stack VLA from 528 KB to 16,896 bytes.

CMakeLists.txt: add Cortex-A72 (CPUPART 0xd08) detection with
-mcpu=cortex-a72 -march=armv8-a+simd (ARMv8.0-A only, no ARMv8.2-A).

ldpctest.c: wire per-phase timers (tinput, tparity, toutput,
tinput_memcpy) into the encoder_implemparams so each phase can be
measured independently.

ldpc_encoder.c / ldpc384_byte_128.c: add code generator and generated
output for a 4-wide 128-bit parity-check variant (ldpc_BG1_Zc384_byte_128_x4)
that unrolls 4 i2 iterations per outer loop; not yet wired into the
call path.

Assisted by Claude Sonnet 4.6
2026-06-06 23:27:28 +02:00
Raymond Knopp
66253f2289 TX optimizations for gNB.
- phase compensation in symbol processing to allow for thread-pool parallelization
- some aarch64 optimizations for precoding and layer mapping
2026-06-06 07:39:19 +02:00
29 changed files with 2402 additions and 230 deletions

View File

@@ -202,6 +202,8 @@ elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -mcpu=neoverse-n1 -lgcc -lrt")
elseif (CPUPART MATCHES "0xd49") # Neoverse-N2
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -mcpu=neoverse-n2 -ftree-vectorize -lgcc -lrt")
elseif (CPUPART MATCHES "0xd08") # Cortex-A72 (e.g. NXP LS2160A)
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -mcpu=cortex-a72 -lgcc -lrt")
elseif (CPUPART MATCHES "0xd03") # Cortex-A53
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -mcpu=cortex-a53 -lgcc -lrt")
else ()
@@ -212,8 +214,11 @@ else()
endif()
if (NOT CROSS_COMPILE)
if (CPUPART MATCHES "0xd03")
# Cortex-A53 does not support ARMv8.2-A extensions
if (CPUPART MATCHES "0xd08")
# Cortex-A72 is ARMv8.0-A only (no ARMv8.2-A); hardware includes crypto (aes, pmull, sha1, sha2)
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -march=armv8-a+simd+crypto")
elseif (CPUPART MATCHES "0xd03")
# Cortex-A53 is ARMv8.0-A only; crypto extension is optional and not assumed present
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -march=armv8-a+simd")
else()
set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -march=native")

View File

@@ -280,6 +280,10 @@ one_measurement_t test_ldpc(short max_iterations,
stop_meas(&time);
impp.n_segments = n_segments;
impp.tinput = &tinput;
impp.tparity = &tparity;
impp.toutput = &toutput;
impp.tinput_memcpy = &tprep;
start_meas(&ret.time_optim);
impp.first_seg = 0;
ldpc_toCompare.LDPCencoder(test_input, channel_input_optim, &impp);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -23,32 +23,40 @@
// high-throughput cases. The version with "permutex2var" instruction uses less memory (i.e. 1/64th of the memory to store the
// input), but uses more reads instead of creating 64 shifts of the input with memcpy
#include "ldpc384_simd512_permutex_byte.c"
#else
#else
#include "ldpc384_simd512_byte.c"
#endif
#else
#include "ldpc384_byte.c"
#endif
#ifdef USE_ALIGNR
#include "ldpc384_alignr_byte_128.c"
#include "ldpc352_alignr_byte_128.c"
#include "ldpc320_alignr_byte_128.c"
#include "ldpc288_alignr_byte_128.c"
#include "ldpc256_alignr_byte_128.c"
#include "ldpc240_alignr_byte_128.c"
#include "ldpc224_alignr_byte_128.c"
#include "ldpc208_alignr_byte_128.c"
#include "ldpc192_alignr_byte_128.c"
#include "ldpc176_alignr_byte_128.c"
#else
#include "ldpc384_byte_128.c"
#endif
#include "ldpc384_byte.c"
#include "ldpc352_byte.c"
#include "ldpc352_byte_128.c"
#include "ldpc320_byte.c"
#include "ldpc320_byte_128.c"
#include "ldpc288_byte.c"
#include "ldpc288_byte_128.c"
#include "ldpc256_byte.c"
#include "ldpc256_byte_128.c"
#include "ldpc240_byte.c"
#include "ldpc224_byte.c"
#include "ldpc224_byte_128.c"
#include "ldpc208_byte.c"
#include "ldpc192_byte.c"
#include "ldpc192_byte_128.c"
#include "ldpc176_byte.c"
#include "ldpc384_byte_128.c"
#include "ldpc352_byte_128.c"
#include "ldpc320_byte_128.c"
#include "ldpc288_byte_128.c"
#include "ldpc256_byte_128.c"
#include "ldpc224_byte_128.c"
#include "ldpc192_byte_128.c"
#endif
#include "ldpc_BG2_Zc384_byte.c"
#include "ldpc_BG2_Zc384_byte_128.c"
#include "ldpc_BG2_Zc352_byte.c"
@@ -82,7 +90,14 @@
static void encode_parity_check_part_optim(uint8_t *cc, uint8_t *d, short BG, short Zc, int simd_size, int ncols, time_stats_t *tinput_memcpy)
{
unsigned char c[2 * 22 * Zc * simd_size] __attribute__((aligned(64))); //double size matrix of c
// For the alignr path (aarch64, BG1, Zc=384) the simd_size copies are skipped,
// so only one copy is needed — avoid a 32x overallocation on the stack.
#ifdef USE_ALIGNR
int vla_simd = (BG == 1 && Zc >= 176) ? 1 : simd_size;
#else
int vla_simd = simd_size;
#endif
unsigned char c[2 * 22 * Zc * vla_simd] __attribute__((aligned(64))); //double size matrix of c
if (tinput_memcpy)
start_meas(tinput_memcpy);
for (int i1 = 0; i1 < ncols; i1++) {
@@ -90,7 +105,7 @@ static void encode_parity_check_part_optim(uint8_t *cc, uint8_t *d, short BG, sh
memcpy(&c[(2 * i1 + 1) * Zc], &cc[i1 * Zc], Zc * sizeof(unsigned char));
}
#if (defined(USE_PERMUTEX) && defined(__AVX512VBMI__)) || defined(USE_ALIGNR)
if (Zc < 384 || BG == 2)
if (BG == 2)
#endif
{
for (int i1 = 1; i1 < simd_size; i1++) {

View File

@@ -36,6 +36,7 @@ int LDPCencoder(unsigned char **inputArray, unsigned char *outputArray, encoder_
int nind=0;
int indlist[1000];
int indlist2[1000];
int indlist3[1000];
const short *Gen_shift_values = choose_generator_matrix(BG, Zc);
if (Gen_shift_values==NULL) {
@@ -193,6 +194,51 @@ int LDPCencoder(unsigned char **inputArray, unsigned char *outputArray, encoder_
}
fclose(fd);
fclose(fd2);
// 4-wide 128-bit variant: process 4 consecutive i2 values per outer iteration.
// Each group of 4 x 16-byte loads to the same base index lands in one 64-byte
// cache line, matching AVX-512 cache utilisation using four NEON registers.
if ((Zc & 63) == 0) {
sprintf(fname,"ldpc_BG%d_Zc%d_byte_128_x4.c",BG,Zc);
FILE *fd3 = fopen(fname,"w");
AssertFatal(fd3!=NULL,"cannot open %s\n",fname);
fprintf(fd3,"#include \"PHY/sse_intrin.h\"\n");
fprintf(fd3,"// generated code for Zc=%d, 4-wide 128-bit byte encoding\n",Zc);
fprintf(fd3,"static inline void ldpc_BG%d_Zc%d_byte_128_x4(uint8_t *c,uint8_t *d) {\n",BG,Zc);
fprintf(fd3," simde__m128i *csimd=(simde__m128i *)c,*dsimd=(simde__m128i *)d;\n");
fprintf(fd3," simde__m128i *c2_0,*c2_1,*c2_2,*c2_3;\n");
fprintf(fd3," simde__m128i *d2_0,*d2_1,*d2_2,*d2_3;\n");
fprintf(fd3," int i2;\n");
fprintf(fd3," for (i2=0; i2<%d; i2+=4) {\n",Zc>>4);
fprintf(fd3," c2_0=&csimd[i2]; c2_1=&csimd[i2+1]; c2_2=&csimd[i2+2]; c2_3=&csimd[i2+3];\n");
fprintf(fd3," d2_0=&dsimd[i2]; d2_1=&dsimd[i2+1]; d2_2=&dsimd[i2+2]; d2_3=&dsimd[i2+3];\n");
for (i1 = 0; i1 < nrows; i1++) {
fprintf(fd3,"\n//row: %d\n",i1);
int row_offset3 = (Zc*i1)>>4;
// recompute index list with 128-bit shift/mask
int nind3 = 0;
for (i3=0; i3 < ncols; i3++) {
int tp = i1 * ncols + i3;
for (i4=0; i4 < no_shift_values[tp]; i4++) {
int v = (int)((i3*Zc + (Gen_shift_values[pointer_shift_values[tp]+i4]+1)%Zc)/Zc);
int idx = v*2*Zc + (i3*Zc + (Gen_shift_values[pointer_shift_values[tp]+i4]+1)%Zc) % Zc;
indlist3[nind3++] = ((idx&15)*((2*Zc*ncols)>>4))+(idx>>4);
}
}
int stream;
for (stream=0; stream<4; stream++) {
fprintf(fd3," d2_%d[%d]=",stream,row_offset3);
for (i4=0; i4<nind3-1; i4++)
fprintf(fd3,"simde_mm_xor_si128(c2_%d[%d],",stream,indlist3[i4]);
fprintf(fd3,"c2_%d[%d]",stream,indlist3[i4]);
for (i4=0; i4<nind3-1; i4++) fprintf(fd3,")");
fprintf(fd3,";\n");
}
}
fprintf(fd3," }\n}\n");
fclose(fd3);
}
}
else if(gen_code==0)
{

View File

@@ -134,28 +134,78 @@ int LDPCencoder(uint8_t **input, uint8_t *output, encoder_implemparams_t *impp)
#endif
#ifdef __aarch64__
simde__m128i shufmask = simde_mm_set_epi64x(0x0101010101010101, 0x0000000000000000);
simde__m128i andmask = simde_mm_set1_epi64x(0x0102040810204080); // every 8 bits -> 8 bytes, pattern repeats.
simde__m128i zero128 = simde_mm_setzero_si128();
simde__m128i masks[8];
register simde__m128i c128;
masks[0] = simde_mm_set1_epi8(0x1);
masks[1] = simde_mm_set1_epi8(0x2);
masks[2] = simde_mm_set1_epi8(0x4);
masks[3] = simde_mm_set1_epi8(0x8);
masks[4] = simde_mm_set1_epi8(0x10);
masks[5] = simde_mm_set1_epi8(0x20);
masks[6] = simde_mm_set1_epi8(0x40);
masks[7] = simde_mm_set1_epi8(0x80);
// Direct NEON: broadcast 1 byte to all 8 lanes (ldr-b + dup), VTST bit-test mask.
// ns=8 fast path: fully unrolled, 2-level asm-volatile barrier OR-tree, no j-loop
// overhead. Barrier 1 forces 4 independent level-1 ORRs; GCC fills the level-2
// ORR latency gap with scalar loop work, letting A72's OoO engine start the next
// iteration's address computation while ORRs are still in flight.
#include <arm_neon.h>
static const uint8_t _amask[16] __attribute__((aligned(16))) =
{0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01,
0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
const uint8x16_t amask_v = vld1q_u8(_amask);
uint8x16_t segmasks[8];
for (int k = 0; k < 8; k++) segmasks[k] = vdupq_n_u8(1u << k);
for (; i_byte < ((block_length >> 4 ) << 4); i_byte += 16) {
unsigned int i = i_byte >> 4;
c128 = simde_mm_and_si128(simde_mm_cmpeq_epi8(simde_mm_andnot_si128(simde_mm_shuffle_epi8(simde_mm_set1_epi16(((uint16_t*)input[macro_segment])[i]), shufmask),andmask),zero128),masks[0]);
for (int j=macro_segment+1; j < macro_segment_end; j++) {
c128 = simde_mm_or_si128(simde_mm_and_si128(simde_mm_cmpeq_epi8(simde_mm_andnot_si128(simde_mm_shuffle_epi8(simde_mm_set1_epi32(((uint16_t*)input[j])[i]), shufmask),andmask),zero128),masks[j-macro_segment]),c128);
#define CONTRIB(p, bi, k) \
vandq_u8(vtstq_u8(vcombine_u8(vld1_dup_u8((p) + (bi)), \
vld1_dup_u8((p) + (bi) + 1)), amask_v), segmasks[k])
int ns = (int)(macro_segment_end - macro_segment);
if (ns == 8) {
// Pre-extract segment base pointers so GCC holds them in registers across the loop.
const uint8_t *p0 = input[macro_segment + 0];
const uint8_t *p1 = input[macro_segment + 1];
const uint8_t *p2 = input[macro_segment + 2];
const uint8_t *p3 = input[macro_segment + 3];
const uint8_t *p4 = input[macro_segment + 4];
const uint8_t *p5 = input[macro_segment + 5];
const uint8_t *p6 = input[macro_segment + 6];
const uint8_t *p7 = input[macro_segment + 7];
for (; i_byte < ((block_length >> 4) << 4); i_byte += 16) {
unsigned int bi = i_byte >> 3;
uint8x16_t r0 = CONTRIB(p0, bi, 0);
uint8x16_t r1 = CONTRIB(p1, bi, 1);
uint8x16_t r2 = CONTRIB(p2, bi, 2);
uint8x16_t r3 = CONTRIB(p3, bi, 3);
uint8x16_t r4 = CONTRIB(p4, bi, 4);
uint8x16_t r5 = CONTRIB(p5, bi, 5);
uint8x16_t r6 = CONTRIB(p6, bi, 6);
uint8x16_t r7 = CONTRIB(p7, bi, 7);
// Barrier 1: force all 8 contributions into physical NEON registers before
// any ORR starts. Without this GCC greedily issues each ORR as soon as ONE
// input is ready, producing a 7-step serial left-fold (28-cycle critical path).
asm volatile ("" : "+w"(r0), "+w"(r1), "+w"(r2), "+w"(r3),
"+w"(r4), "+w"(r5), "+w"(r6), "+w"(r7));
// Level-1 ORRs: all 4 are data-independent; A72's 2-wide NEON issues pairs/cycle.
uint8x16_t r01 = vorrq_u8(r0, r1);
uint8x16_t r23 = vorrq_u8(r2, r3);
uint8x16_t r45 = vorrq_u8(r4, r5);
uint8x16_t r67 = vorrq_u8(r6, r7);
// Barrier 2: force all level-1 results into registers before level-2.
// GCC's scheduler then fills the level-2 ORR latency gap with the loop's
// scalar work (add i_byte, cmp), placing those instructions between the
// level-2 and level-3/4 ORRs. This lets the A72's OoO engine start the
// next iteration's address computation (lsr bi = i_byte>>3) ~4 cycles earlier
// than if the scalar work came after all ORRs, hiding load latency for free.
asm volatile ("" : "+w"(r01), "+w"(r23), "+w"(r45), "+w"(r67));
vst1q_u8(cc + i_byte, vorrq_u8(vorrq_u8(r01, r23), vorrq_u8(r45, r67)));
}
} else {
uint8x16_t c128_v, vv;
for (; i_byte < ((block_length >> 4) << 4); i_byte += 16) {
unsigned int bi = i_byte >> 3;
vv = vcombine_u8(vld1_dup_u8(input[macro_segment] + bi),
vld1_dup_u8(input[macro_segment] + bi + 1));
c128_v = vandq_u8(vtstq_u8(vv, amask_v), segmasks[0]);
for (int j = macro_segment + 1; j < macro_segment_end; j++) {
vv = vcombine_u8(vld1_dup_u8(input[j] + bi), vld1_dup_u8(input[j] + bi + 1));
c128_v = vorrq_u8(vandq_u8(vtstq_u8(vv, amask_v), segmasks[j - macro_segment]), c128_v);
}
vst1q_u8(cc + i_byte, c128_v);
}
((simde__m128i *)cc)[i] = c128;
}
#undef CONTRIB
#endif
for (; i_byte < block_length; i_byte++) {

View File

@@ -215,6 +215,174 @@ void nr_modulation(const uint32_t *in, uint32_t length, uint16_t mod_order, int1
AssertFatal(false, "Invalid or unsupported modulation order %d\n", mod_order);
}
static inline uint8_t get_packed_symbol(const uint8_t *in_bytes, uint32_t length, uint16_t mod_order, uint32_t symbol_idx)
{
const uint32_t bit_offset = symbol_idx * mod_order;
const uint32_t byte_offset = bit_offset >> 3;
const uint32_t bit_shift = bit_offset & 0x7;
const uint32_t num_bytes = (length + 7) >> 3;
uint16_t packed = in_bytes[byte_offset];
if (bit_shift > 8 - mod_order && byte_offset + 1 < num_bytes)
packed |= (uint16_t)in_bytes[byte_offset + 1] << 8;
return (packed >> bit_shift) & ((1U << mod_order) - 1);
}
static inline uint64_t get_packed_bits(const uint8_t *in_bytes, uint32_t length, uint32_t bit_offset, uint8_t width)
{
const uint32_t byte_offset = bit_offset >> 3;
const uint32_t bit_shift = bit_offset & 0x7;
const uint32_t num_bytes = (length + 7) >> 3;
const uint32_t bytes_needed = (bit_shift + width + 7) >> 3;
uint64_t packed = 0;
for (uint32_t i = 0; i < bytes_needed && byte_offset + i < num_bytes; i++)
packed |= (uint64_t)in_bytes[byte_offset + i] << (i << 3);
return (packed >> bit_shift) & ((UINT64_C(1) << width) - 1);
}
bool nr_modulation_layer_mapping(const uint32_t *in,
uint32_t length,
uint16_t mod_order,
uint8_t n_layers,
int layerSz,
c16_t tx_layers[][layerSz])
{
if (n_layers < 1 || n_layers > 4)
return false;
const uint32_t n_symbs = length / mod_order;
if ((n_symbs % n_layers) != 0)
return false;
const uint8_t *in_bytes = (const uint8_t *)in;
switch (mod_order) {
case 2: {
const c16_t *nr_mod_table = nr_qpsk_mod_table;
if (n_layers == 1) {
nr_modulation(in, length, mod_order, (int16_t *)tx_layers[0]);
return true;
}
for (uint32_t sym = 0, layer_sym = 0; sym < n_symbs; sym += n_layers, layer_sym++) {
for (uint8_t layer = 0; layer < n_layers; layer++) {
const uint8_t idx = get_packed_symbol(in_bytes, length, mod_order, sym + layer);
tx_layers[layer][layer_sym] = nr_mod_table[idx];
}
}
return true;
}
case 4: {
const int32_t *nr_mod_table = nr_16qam_mod_table;
if (n_layers == 1) {
nr_modulation(in, length, mod_order, (int16_t *)tx_layers[0]);
return true;
}
for (uint32_t sym = 0, layer_sym = 0; sym < n_symbs; sym += n_layers, layer_sym++) {
for (uint8_t layer = 0; layer < n_layers; layer++) {
const uint8_t idx = get_packed_symbol(in_bytes, length, mod_order, sym + layer);
((int32_t *)tx_layers[layer])[layer_sym] = nr_mod_table[idx];
}
}
return true;
}
case 6: {
const c16_t *nr_mod_table = (const c16_t *)nr_64qam_mod_table;
if (n_layers == 1) {
c16_t *tx0 = tx_layers[0];
uint32_t sym = 0;
for (; sym + 2 <= n_symbs; sym += 2) {
const uint16_t idx = get_packed_bits(in_bytes, length, sym * mod_order, 12);
tx0[sym] = nr_mod_table[idx * 2];
tx0[sym + 1] = nr_mod_table[idx * 2 + 1];
}
if (sym < n_symbs) {
const uint8_t idx = get_packed_symbol(in_bytes, length, mod_order, sym);
tx0[sym] = nr_mod_table[idx * 2];
}
return true;
}
if (n_layers == 2) {
c16_t *tx0 = tx_layers[0];
c16_t *tx1 = tx_layers[1];
for (uint32_t sym = 0, layer_sym = 0; sym < n_symbs; sym += 2, layer_sym++) {
const uint16_t idx = get_packed_bits(in_bytes, length, sym * mod_order, 12);
tx0[layer_sym] = nr_mod_table[idx * 2];
tx1[layer_sym] = nr_mod_table[idx * 2 + 1];
}
return true;
}
if (n_layers == 3) {
c16_t *tx0 = tx_layers[0];
c16_t *tx1 = tx_layers[1];
c16_t *tx2 = tx_layers[2];
uint32_t sym = 0;
uint32_t layer_sym = 0;
for (; sym + 6 <= n_symbs; sym += 6, layer_sym += 2) {
const uint64_t bits = get_packed_bits(in_bytes, length, sym * mod_order, 36);
const uint16_t idx0 = bits & 0xfff;
const uint16_t idx1 = (bits >> 12) & 0xfff;
const uint16_t idx2 = (bits >> 24) & 0xfff;
tx0[layer_sym] = nr_mod_table[idx0 * 2];
tx1[layer_sym] = nr_mod_table[idx0 * 2 + 1];
tx2[layer_sym] = nr_mod_table[idx1 * 2];
tx0[layer_sym + 1] = nr_mod_table[idx1 * 2 + 1];
tx1[layer_sym + 1] = nr_mod_table[idx2 * 2];
tx2[layer_sym + 1] = nr_mod_table[idx2 * 2 + 1];
}
if (sym < n_symbs) {
for (uint8_t layer = 0; layer < 3; layer++) {
const uint8_t idx = get_packed_symbol(in_bytes, length, mod_order, sym + layer);
tx_layers[layer][layer_sym] = nr_mod_table[idx * 2];
}
}
return true;
}
if (n_layers == 4) {
c16_t *tx0 = tx_layers[0];
c16_t *tx1 = tx_layers[1];
c16_t *tx2 = tx_layers[2];
c16_t *tx3 = tx_layers[3];
for (uint32_t sym = 0, layer_sym = 0; sym < n_symbs; sym += 4, layer_sym++) {
const uint32_t bits = get_packed_bits(in_bytes, length, sym * mod_order, 24);
const uint16_t idx0 = bits & 0xfff;
const uint16_t idx1 = (bits >> 12) & 0xfff;
tx0[layer_sym] = nr_mod_table[idx0 * 2];
tx1[layer_sym] = nr_mod_table[idx0 * 2 + 1];
tx2[layer_sym] = nr_mod_table[idx1 * 2];
tx3[layer_sym] = nr_mod_table[idx1 * 2 + 1];
}
return true;
}
return false;
}
case 8: {
const int32_t *nr_mod_table = nr_256qam_mod_table;
if (n_layers == 1) {
nr_modulation(in, length, mod_order, (int16_t *)tx_layers[0]);
return true;
}
for (uint32_t sym = 0, layer_sym = 0; sym < n_symbs; sym += n_layers, layer_sym++) {
for (uint8_t layer = 0; layer < n_layers; layer++) {
const uint8_t idx = get_packed_symbol(in_bytes, length, mod_order, sym + layer);
((int32_t *)tx_layers[layer])[layer_sym] = nr_mod_table[idx];
}
}
return true;
}
default:
return false;
}
}
void nr_layer_mapping(int nbCodes,
int encoded_len,
c16_t mod_symbs[nbCodes][encoded_len],
@@ -257,16 +425,12 @@ void nr_layer_mapping(int nbCodes,
}
#endif
#if defined(__aarch64__) && defined(USE_NEON)
// SIMDe doesn't handle this properly, gcc up to 14.2 neither
uint8_t const perm0[16] = {0, 1, 2, 3, 8, 9, 10, 11, 4, 5, 6, 7, 12, 13, 14, 15};
uint8x16_t perm = vld1q_u8(perm0);
uint8x16_t d;
for (; i < (n_symbs & (~3)); i += 4) {
d = vqtbl1q_u8(*(uint8x16_t *)(mod + i), perm);
*(int64_t *)tx0 = vgetq_lane_u64((uint64x2_t)d, 0);
*(int64_t *)tx1 = vgetq_lane_u64((uint64x2_t)d, 1);
tx0 += 2;
tx1 += 2;
for (; i < (n_symbs & ~7); i += 8) {
uint32x4x2_t d = vld2q_u32((const uint32_t *)(mod + i));
vst1q_u32((uint32_t *)tx0, d.val[0]);
vst1q_u32((uint32_t *)tx1, d.val[1]);
tx0 += 4;
tx1 += 4;
}
#endif
for (; i < n_symbs; i += 2) {
@@ -375,6 +539,17 @@ void nr_layer_mapping(int nbCodes,
tx2 += 8;
}
}
#endif
#if defined(__aarch64__) && defined(USE_NEON)
for (; i < (n_symbs & ~11); i += 12) {
uint32x4x3_t d = vld3q_u32((const uint32_t *)(mod + i));
vst1q_u32((uint32_t *)tx0, d.val[0]);
vst1q_u32((uint32_t *)tx1, d.val[1]);
vst1q_u32((uint32_t *)tx2, d.val[2]);
tx0 += 4;
tx1 += 4;
tx2 += 4;
}
#endif
for (; i < n_symbs; i += 3) {
*tx0++ = mod[i];
@@ -448,17 +623,16 @@ void nr_layer_mapping(int nbCodes,
}
#endif
#if defined(__aarch64__) && defined(USE_NEON)
// SIMDe doesn't handle this properly, gcc up to 14.2 neither
for (; i < (n_symbs & ~3); i += 4) {
uint32x4_t d4 = *(uint32x4_t *)(mod + i);
*(uint32_t *)tx0 = vgetq_lane_u32(d4, 0);
tx0++;
*(uint32_t *)tx1 = vgetq_lane_u32(d4, 1);
tx1++;
*(uint32_t *)tx2 = vgetq_lane_u32(d4, 2);
tx2++;
*(uint32_t *)tx3 = vgetq_lane_u32(d4, 3);
tx3++;
for (; i < (n_symbs & ~15); i += 16) {
uint32x4x4_t d = vld4q_u32((const uint32_t *)(mod + i));
vst1q_u32((uint32_t *)tx0, d.val[0]);
vst1q_u32((uint32_t *)tx1, d.val[1]);
vst1q_u32((uint32_t *)tx2, d.val[2]);
vst1q_u32((uint32_t *)tx3, d.val[3]);
tx0 += 4;
tx1 += 4;
tx2 += 4;
tx3 += 4;
}
#endif
for (; i < n_symbs; i += 4) {
@@ -740,21 +914,47 @@ static inline __attribute__((always_inline)) __m256i cmac_prec256(__m256i y, __m
}
#endif
#ifdef __aarch64__
#ifdef NEWOPTIM
static inline __attribute__((always_inline)) int16x4x2_t cmac0_prec4(int16x8_t x, int16x4_t wr, int16x4_t wi) {
const int16x4_t x_lo = vget_low_s16(x);
const int16x4_t x_hi = vget_high_s16(x);
const int16x4_t xr = vuzp1_s16(x_lo, x_hi);
const int16x4_t xi = vuzp2_s16(x_lo, x_hi);
#else
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
#endif
#ifdef __ARM_FEATURE_QRDMX
// ARMv8.1-A: Use RDM instructions
#ifdef NEWOPTIM
// real = ar*br - ai*bi (Q15 scaling via high-half doubling muls)
int16x4_t real = vqdmulh_s16(xr, wr); // ≈ round((2*xr*wr)/2^16)
real = vqrdmlsh_s16(real, xi, wi); // real -= round((2*xi*wi)/2^16)
// imag = ar*bi + ai*br
int16x4_t imag = vqdmulh_s16(xr, wi);
imag = vqrdmlah_s16(imag, xi, wr); // imag += round((2*xi*wr)/2^16)
#else
// 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)
#endif
#else
// ARMv8.0-A fallback: Use standard 32-bit multiply
#ifdef NEWOPTIM
int32x4_t real_prod = vmull_s16(xr, wr);
real_prod = vmlsl_s16(real_prod, xi, wi);
int32x4_t imag_prod = vmull_s16(xr, wi);
imag_prod = vmlal_s16(imag_prod, xi, wr);
int16x4_t real = vqrshrn_n_s32(real_prod, 15);
int16x4_t imag = vqrshrn_n_s32(imag_prod, 15);
#else
int32x4_t real_lo = vmull_s16(vget_low_s16(xr), vget_low_s16(wr));
int32x4_t real_hi = vmull_s16(vget_high_s16(xr), vget_high_s16(wr));
real_lo = vmlsl_s16(real_lo, vget_low_s16(xi), vget_low_s16(wi));
@@ -768,15 +968,33 @@ static inline __attribute__((always_inline)) int16x8_t cmac0_prec128(int16x8_t x
int16x8_t real = vcombine_s16(vqrshrn_n_s32(real_lo, 15), vqrshrn_n_s32(real_hi, 15));
int16x8_t imag = vcombine_s16(vqrshrn_n_s32(imag_lo, 15), vqrshrn_n_s32(imag_hi, 15));
#endif
#endif
#ifdef NEWOPTIM
int16x4x2_t produ;
produ.val[0] = real;
produ.val[1] = imag;
return produ;
#else
// Re-interleave [real, imag]
int16x8x2_t produ = vzipq_s16(real, imag);
return produ.val[0];
return produ.val[0];
#endif
}
#ifdef NEWOPTIM
static inline __attribute__((always_inline)) int16x4x2_t cmac_prec4(int16x4x2_t y, int16x8_t x, int16x4_t wr, int16x4_t wi) {
const int16x4x2_t produ = cmac0_prec4(x, wr, wi);
y.val[0] = vadd_s16(y.val[0], produ.val[0]);
y.val[1] = vadd_s16(y.val[1], produ.val[1]);
return y;
}
#else
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
#endif
#else // __x86 128-bit
static inline __attribute__((always_inline)) simde__m128i cmac0_prec128(simde__m128i x, simde__m128i w_c, simde__m128i w_s)
{
// Multiplication and shift
@@ -807,6 +1025,12 @@ static inline __attribute__((always_inline)) __m128i cmac_prec128(__m128i y, __m
const Type w_c##Rank = Instruct(c16toI32(c16conj(weights[Rank][ant]))); \
const Type w_s##Rank = Instruct(c16toI32(c16swap(weights[Rank][ant]))); \
const Type *in##Rank = (Type *)(txdataF_res_mapped[Rank] + sc_offset + (out-beginning));
#if defined(NEWOPTIM) && defined(__aarch64__)
#define load_consts_arm(Rank) \
const int16x4_t wr##Rank = vdup_n_s16(weights[Rank][ant].r); \
const int16x4_t wi##Rank = vdup_n_s16(weights[Rank][ant].i); \
const int16x8_t *in##Rank = (const int16x8_t *)(txdataF_res_mapped[Rank] + sc_offset + (out - beginning));
#endif
void nr_layer_precoder_simd(const int n_layers,
const int symSz,
@@ -943,19 +1167,37 @@ void nr_layer_precoder_simd(const int n_layers,
}
#endif
#ifdef __aarch64__
load_consts(int16x8_t, vdupq_n_s16, 0);
#ifdef NEWOPTIM
load_consts_arm(0);
#else
load_consts(int16x8_t, vdupq_n_s16, 0);
#endif
if (n_layers == 1) {
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
#ifdef NEWOPTIM
const int16x4x2_t y = cmac0_prec4(vld1q_s16((const int16_t *)in0++), wr0, wi0);
vst2_s16((int16_t *)out, y);
#else
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;
#endif
}
}
if (n_layers == 2) {
load_consts(int16x8_t, vdupq_n_s16, 1);
#ifdef NEWOPTIM
load_consts_arm(1);
#else
load_consts(int16x8_t, vdupq_n_s16, 1);
#endif
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
#ifdef NEWOPTIM
int16x4x2_t y = cmac0_prec4(vld1q_s16((const int16_t *)in0++), wr0, wi0);
y = cmac_prec4(y, vld1q_s16((const int16_t *)in1++), wr1, wi1);
vst2_s16((int16_t *)out, y);
#else
const int16x8_t x0 = vld1q_s16((const int16_t *)in0++);
const int16x8_t x1 = vld1q_s16((const int16_t *)in1++);
// Accumulate the product
@@ -963,41 +1205,66 @@ void nr_layer_precoder_simd(const int n_layers,
y = cmac_prec128(y, x1, w_c1, w_s1);
// Store the result to txdataF
*(int16x8_t *)out = y;
#endif
}
}
if (n_layers == 3) {
#ifdef NEWOPTIM
load_consts_arm(1);
load_consts_arm(2);
#else
load_consts(int16x8_t, vdupq_n_s16, 1);
load_consts(int16x8_t, vdupq_n_s16, 2);
#endif
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
#ifdef NEWOPTIM
int16x4x2_t y = cmac0_prec4(vld1q_s16((const int16_t *)in0++), wr0, wi0);
y = cmac_prec4(y, vld1q_s16((const int16_t *)in1++), wr1, wi1);
y = cmac_prec4(y, vld1q_s16((const int16_t *)in2++), wr2, wi2);
vst2_s16((int16_t *)out, y);
#else
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;
#endif
}
}
if (n_layers == 4) {
#ifdef NEWOPTIM
load_consts_arm(1);
load_consts_arm(2);
load_consts_arm(3);
#else
load_consts(int16x8_t, vdupq_n_s16, 1);
load_consts(int16x8_t, vdupq_n_s16, 2);
load_consts(int16x8_t, vdupq_n_s16, 3);
#endif
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
#ifdef NEWOPTIM
int16x4x2_t y = cmac0_prec4(vld1q_s16((const int16_t *)in0++), wr0, wi0);
y = cmac_prec4(y, vld1q_s16((const int16_t *)in1++), wr1, wi1);
y = cmac_prec4(y, vld1q_s16((const int16_t *)in2++), wr2, wi2);
y = cmac_prec4(y, vld1q_s16((const int16_t *)in3++), wr3, wi3);
vst2_s16((int16_t *)out, y);
#else
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;
*(int16x8_t *)out = y;
#endif
}
}
#else

View File

@@ -28,6 +28,13 @@ void nr_modulation(const uint32_t *in,
uint16_t mod_order,
int16_t *out);
bool nr_modulation_layer_mapping(const uint32_t *in,
uint32_t length,
uint16_t mod_order,
uint8_t n_layers,
int layerSz,
c16_t tx_layers[][layerSz]);
/*! \brief Perform NR layer mapping. TS 38.211 V15.4.0 subclause 7.3.1.3
@param[in] mod_symbs, double Pointer to modulated symbols for each codeword
@param[in] n_layers, number of layers

View File

@@ -40,11 +40,26 @@ static inline uint16_t get_dci_ant_port_index(const nfapi_v4_pdcch_pdu_parameter
return dci_ant_idx;
}
static inline void mark_prb(uint64_t *prb_mask, int prb_mask_words, int symbol, int prb)
{
uint64_t *symbol_mask = prb_mask + symbol * prb_mask_words;
symbol_mask[prb >> 6] |= UINT64_C(1) << (prb & 63);
}
static inline bool prb_marked(const uint64_t *prb_mask, int prb)
{
return (prb_mask[prb >> 6] >> (prb & 63)) & 0x1;
}
void nr_generate_dci(PHY_VARS_gNB *gNB,
const nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15,
NR_DL_FRAME_PARMS *frame_parms,
int slot)
int slot,
uint64_t *phase_comp_prb_mask,
int prb_mask_words)
{
uint64_t local_phase_comp_prb_mask[frame_parms->symbols_per_slot][prb_mask_words];
memset(local_phase_comp_prb_mask, 0, sizeof(local_phase_comp_prb_mask));
// fill reg list per symbol
int reg_list[MAX_DCI_CORESET][NR_MAX_PDCCH_AGG_LEVEL * NR_NB_REG_PER_CCE];
nr_fill_reg_list(reg_list, pdcch_pdu_rel15);
@@ -193,6 +208,7 @@ void nr_generate_dci(PHY_VARS_gNB *gNB,
dmrs_idx = (reg_list[d][reg_count] + pdcch_pdu_rel15->BWPStart + rb_offset) * 3;
else
dmrs_idx = (reg_list[d][reg_count] + rb_offset) * 3;
const int reg_prb = pdcch_pdu_rel15->BWPStart + reg_list[d][reg_count] + rb_offset;
int k_prime = 0;
@@ -220,6 +236,8 @@ void nr_generate_dci(PHY_VARS_gNB *gNB,
k++;
} // m
if (gNB->phase_comp)
mark_prb(&local_phase_comp_prb_mask[0][0], prb_mask_words, l, reg_prb);
} // reg_count
} // symbol_idx
@@ -228,4 +246,31 @@ void nr_generate_dci(PHY_VARS_gNB *gNB,
dci_pdu->PayloadSizeBits,
*(unsigned long long *)dci_pdu->Payload);
} // for (int d=0;d<pdcch_pdu_rel15->numDlDci;d++)
if (!gNB->phase_comp)
return;
const int symb_offset = (slot % frame_parms->slots_per_subframe) * frame_parms->symbols_per_slot;
c16_t *txdataF = gNB->common_vars.txdataF[0];
for (int symbol = pdcch_pdu_rel15->StartSymbolIndex;
symbol < pdcch_pdu_rel15->StartSymbolIndex + pdcch_pdu_rel15->DurationSymbols;
symbol++) {
uint64_t *local_symbol_mask = &local_phase_comp_prb_mask[symbol][0];
int prb = 0;
while (prb < frame_parms->N_RB_DL) {
while (prb < frame_parms->N_RB_DL && !prb_marked(local_symbol_mask, prb))
prb++;
const int start_prb = prb;
while (prb < frame_parms->N_RB_DL && prb_marked(local_symbol_mask, prb))
prb++;
if (prb > start_prb) {
c16_t *this_symbol = txdataF + symbol * frame_parms->ofdm_symbol_size + start_prb * NR_NB_SC_PER_RB;
const c16_t *rot = &frame_parms->symbol_rotation[0][symb_offset + symbol];
rotate_cpx_vector(this_symbol, rot, this_symbol, (prb - start_prb) * NR_NB_SC_PER_RB, 15);
uint64_t *global_symbol_mask = phase_comp_prb_mask;
for (int rr = start_prb; rr < prb; rr++)
global_symbol_mask[rr >> 6] |= UINT64_C(1) << (rr & 63);
}
}
}
}

View File

@@ -12,7 +12,9 @@
void nr_generate_dci(PHY_VARS_gNB *gNB,
const nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15,
NR_DL_FRAME_PARMS *frame_parms,
int slot);
int slot,
uint64_t *phase_comp_prb_mask,
int prb_mask_words);
void nr_fill_dci(PHY_VARS_gNB *gNB,
int frame,

View File

@@ -500,12 +500,21 @@ typedef struct pdschSymbolProc_s {
unsigned int dlPtrsSymPos;
unsigned int n_ptrs;
uint16_t *ant_to_map;
uint64_t *pdsch_phase_comp_prb_mask;
int prb_mask_words;
unsigned int re_beginning_of_symbol[14];
c16_t *tx_layers[4];
time_stats_t dlsch_resource_mapping_stats;
time_stats_t dlsch_precoding_stats;
} pdschSymbolProc_t;
static inline void mark_prb_range(uint64_t *prb_mask, int prb_mask_words, int symbol, int start_prb, int nb_prb)
{
uint64_t *symbol_mask = prb_mask + symbol * prb_mask_words;
for (int prb = start_prb; prb < start_prb + nb_prb; prb++)
symbol_mask[prb >> 6] |= UINT64_C(1) << (prb & 63);
}
static void nr_pdsch_symbol_processing(void *arg)
{
pdschSymbolProc_t *rdata = (pdschSymbolProc_t *)arg;
@@ -525,6 +534,7 @@ static void nr_pdsch_symbol_processing(void *arg)
const int symbol_sz = frame_parms->ofdm_symbol_size;
c16_t **txdataF = gNB->common_vars.txdataF;
const int symb_offset = (slot % frame_parms->slots_per_subframe) * frame_parms->symbols_per_slot;
for (int l_symbol = rdata->startSymbol; l_symbol < rdata->startSymbol + rdata->numSymbols; l_symbol++) {
start_meas(&rdata->dlsch_resource_mapping_stats);
@@ -595,6 +605,12 @@ static void nr_pdsch_symbol_processing(void *arg)
block_end - block_start + 1,
txdataF_offset_per_symbol);
if (gNB->phase_comp) {
c16_t *pdsch_sc = &txdataF[ant][txdataF_offset_per_symbol + block_start];
const c16_t *rot = &frame_parms->symbol_rotation[0][symb_offset + l_symbol];
rotate_cpx_vector(pdsch_sc, rot, pdsch_sc,(block_end - block_start + 1)*NR_NB_SC_PER_RB, 15);
mark_prb_range(rdata->pdsch_phase_comp_prb_mask, rdata->prb_mask_words, l_symbol, block_start, block_end - block_start + 1);
}
}
}
stop_meas(&rdata->dlsch_precoding_stats);
@@ -603,7 +619,12 @@ static void nr_pdsch_symbol_processing(void *arg)
completed_task_ans(rdata->ans);
}
static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSCH_t *dlsch, int slot)
static int do_one_dlsch(unsigned char *input_ptr,
PHY_VARS_gNB *gNB,
NR_gNB_DLSCH_t *dlsch,
int slot,
uint64_t *pdsch_phase_comp_prb_mask,
int prb_mask_words)
{
NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
@@ -652,51 +673,67 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC
if (IS_SOFTMODEM_DLSIM)
memcpy(dlsch->f, input_ptr, (encoded_length + 7) >> 3);
c16_t mod_symbs[rel15->NrOfCodewords][encoded_length] __attribute__((aligned(64)));
for (int codeWord = 0; codeWord < rel15->NrOfCodewords; codeWord++) {
/// scrambling
start_meas(dlsch_scrambling_stats);
uint32_t scrambled_output[(encoded_length >> 5) + 4]; // modulator acces by 4 bytes in some cases
memset(scrambled_output, 0, sizeof(scrambled_output));
nr_pdsch_codeword_scrambling(input_ptr, encoded_length, codeWord, rel15->dataScramblingId, rel15->rnti, scrambled_output);
#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[(i << 3) + j]);
printf("\n");
}
#endif
stop_meas(dlsch_scrambling_stats);
/// Modulation
start_meas(dlsch_modulation_stats);
nr_modulation(scrambled_output, encoded_length, Qm, (int16_t *)mod_symbs[codeWord]);
stop_meas(dlsch_modulation_stats);
#ifdef DEBUG_DLSCH
printf("PDSCH Modulation: Qm %d(%d)\n", Qm, nb_re);
for (int i = 0; i < nb_re; i += 8) {
for (int j = 0; j < 8; j++) {
printf("%d %d\t", mod_symbs[codeWord][i + j].r, mod_symbs[codeWord][i + j].i);
}
printf("\n");
}
#endif
}
start_meas(&gNB->dlsch_pdsch_generation_stats);
/// Resource mapping
// Non interleaved VRB to PRB mapping
AssertFatal(n_dmrs, "n_dmrs can't be 0\n");
// make a large enough tail to process all re with SIMD regardless a garbadge filler
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));
nr_layer_mapping(rel15->NrOfCodewords, encoded_length, mod_symbs, rel15->nrOfLayers, layerSz2, nb_re, tx_layers);
const bool use_fused_mod_layer =
rel15->NrOfCodewords == 1 && (rel15->nrOfLayers == 3 || rel15->nrOfLayers == 4) && (Qm == 2 || Qm == 4 || Qm == 6 || Qm == 8);
if (use_fused_mod_layer) {
start_meas(dlsch_scrambling_stats);
uint32_t scrambled_output[(encoded_length >> 5) + 4]; // modulator access by 4 bytes in some cases
memset(scrambled_output, 0, sizeof(scrambled_output));
start_meas(dlsch_modulation_stats);
nr_pdsch_codeword_scrambling(input_ptr, encoded_length, 0, rel15->dataScramblingId, rel15->rnti, scrambled_output);
stop_meas(dlsch_scrambling_stats);
const bool fused_ok =
nr_modulation_layer_mapping(scrambled_output, encoded_length, Qm, rel15->nrOfLayers, layerSz2, tx_layers);
AssertFatal(fused_ok,
"Unsupported fused modulation/layer mapping for Qm %d, %d layers, %d codewords\n",
Qm,
rel15->nrOfLayers,
rel15->NrOfCodewords);
stop_meas(dlsch_modulation_stats);
} else {
c16_t mod_symbs[rel15->NrOfCodewords][encoded_length] __attribute__((aligned(64)));
for (int codeWord = 0; codeWord < rel15->NrOfCodewords; codeWord++) {
/// scrambling
start_meas(dlsch_scrambling_stats);
uint32_t scrambled_output[(encoded_length >> 5) + 4]; // modulator acces by 4 bytes in some cases
memset(scrambled_output, 0, sizeof(scrambled_output));
nr_pdsch_codeword_scrambling(input_ptr, encoded_length, codeWord, rel15->dataScramblingId, rel15->rnti, scrambled_output);
#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[(i << 3) + j]);
printf("\n");
}
#endif
stop_meas(dlsch_scrambling_stats);
/// Modulation
start_meas(dlsch_modulation_stats);
nr_modulation(scrambled_output, encoded_length, Qm, (int16_t *)mod_symbs[codeWord]);
stop_meas(dlsch_modulation_stats);
#ifdef DEBUG_DLSCH
printf("PDSCH Modulation: Qm %d(%d)\n", Qm, nb_re);
for (int i = 0; i < nb_re; i += 8) {
for (int j = 0; j < 8; j++) {
printf("%d %d\t", mod_symbs[codeWord][i + j].r, mod_symbs[codeWord][i + j].i);
}
printf("\n");
}
#endif
}
start_meas(&gNB->dlsch_layer_mapping_stats);
nr_layer_mapping(rel15->NrOfCodewords, encoded_length, mod_symbs, rel15->nrOfLayers, layerSz2, nb_re, tx_layers);
stop_meas(&gNB->dlsch_layer_mapping_stats);
}
/// Layer Precoding and Antenna port mapping
// tx_layers 1-8 are mapped on antenna ports 1000-1007
@@ -765,6 +802,8 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC
rdata->dlPtrsSymPos = dlPtrsSymPos;
rdata->n_ptrs = n_ptrs;
rdata->ant_to_map = ant_to_map;
rdata->pdsch_phase_comp_prb_mask = pdsch_phase_comp_prb_mask;
rdata->prb_mask_words = prb_mask_words;
for (int s = l_symbol; s < l_symbol + rdata->numSymbols; s++) {
rdata->re_beginning_of_symbol[s] = re_beginning_of_symbol;
re_beginning_of_symbol += freq_alloc->num_rbs * NR_NB_SC_PER_RB;
@@ -798,7 +837,13 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC
return ((size_output_tb + 511) >> 9) << 6;
}
void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_array, int frame, int slot)
void nr_generate_pdsch(PHY_VARS_gNB *gNB,
int n_dlsch,
NR_gNB_DLSCH_t *dlsch_array,
int frame,
int slot,
uint64_t *pdsch_phase_comp_prb_mask,
int prb_mask_words)
{
time_stats_t *dlsch_encoding_stats = &gNB->dlsch_encoding_stats;
time_stats_t *tinput = &gNB->tinput;
@@ -810,6 +855,7 @@ void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_arr
time_stats_t *dlsch_rate_matching_stats = &gNB->dlsch_rate_matching_stats;
time_stats_t *dlsch_interleaving_stats = &gNB->dlsch_interleaving_stats;
time_stats_t *dlsch_segmentation_stats = &gNB->dlsch_segmentation_stats;
time_stats_t *dlsch_crc_stats = &gNB->dlsch_crc_stats;
size_t size_output = 0;
@@ -876,7 +922,8 @@ void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_arr
tconcat,
dlsch_rate_matching_stats,
dlsch_interleaving_stats,
dlsch_segmentation_stats)
dlsch_segmentation_stats,
dlsch_crc_stats)
== -1) {
return;
}
@@ -884,7 +931,7 @@ void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_arr
unsigned char *output_ptr = output;
for (int i = 0; i < n_dlsch; i++) {
output_ptr += do_one_dlsch(output_ptr, gNB, &dlsch_array[i], slot);
output_ptr += do_one_dlsch(output_ptr, gNB, &dlsch_array[i], slot, pdsch_phase_comp_prb_mask, prb_mask_words);
}
}

View File

@@ -11,7 +11,13 @@
#include "PHY/defs_gNB.h"
void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_array, int frame, int slot);
void nr_generate_pdsch(PHY_VARS_gNB *gNB,
int n_dlsch,
NR_gNB_DLSCH_t *dlsch_array,
int frame,
int slot,
uint64_t *pdsch_phase_comp_prb_mask,
int prb_mask_words);
int nr_dlsch_encoding(PHY_VARS_gNB *gNB,
int n_dlsch,
@@ -27,7 +33,8 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB,
time_stats_t *tconcat,
time_stats_t *dlsch_rate_matching_stats,
time_stats_t *dlsch_interleaving_stats,
time_stats_t *dlsch_segmentation_stats);
time_stats_t *dlsch_segmentation_stats,
time_stats_t *dlsch_crc_stats);
void dump_pdsch_stats(FILE *fd,PHY_VARS_gNB *gNB);

View File

@@ -98,7 +98,8 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB,
time_stats_t *tconcat,
time_stats_t *dlsch_rate_matching_stats,
time_stats_t *dlsch_interleaving_stats,
time_stats_t *dlsch_segmentation_stats)
time_stats_t *dlsch_segmentation_stats,
time_stats_t *dlsch_crc_stats)
{
nrLDPC_TB_encoding_parameters_t TBs[n_dlsch];
memset(TBs, 0, sizeof(TBs));
@@ -140,6 +141,7 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB,
phy_stats->dlsch_stats.current_Qm = rel15->qamModOrder[0];
}
start_meas(dlsch_crc_stats);
int max_bytes = MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER * rel15->nrOfLayers * 1056;
int B;
if (A > NR_MAX_PDSCH_TBS) {
@@ -165,6 +167,7 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB,
memcpy(dlsch->b, a, (A / 8) + 3); // using 3 bytes to mimic the case of 24 bit crc
}
stop_meas(dlsch_crc_stats);
nrLDPC_TB_encoding_parameters_t *TB_parameters = &TBs[i];
// The harq_pid is not unique among the active HARQ processes in the instance so we use i instead

View File

@@ -16,11 +16,11 @@ void nr_codeword_scrambling(uint8_t *in,
const int roundedSz = (size + 31) / 32;
uint32_t *seq = gold_cache((n_RNTI << 15) + (q << 14) + Nid, roundedSz);
unsigned int i_32 = 0;
#if defined(__AVX512F__) && defined(__AVX512VL__)
#ifdef __AVX2__
for (; i_32 < ((roundedSz >> 3) << 3); i_32 += 8) {
__m256i in_256 = _mm256_load_epi32(&((uint32_t *)in)[i_32]);
__m256i seq_256 = _mm256_load_epi32(&seq[i_32]);
_mm256_storeu_epi32(&out[i_32], _mm256_xor_si256(in_256, seq_256));
__m256i in_256 = _mm256_loadu_si256((void*)&((uint32_t *)in)[i_32]);
__m256i seq_256 = _mm256_loadu_si256((void*)&seq[i_32]);
_mm256_storeu_si256((void*)&out[i_32], _mm256_xor_si256(in_256, seq_256));
}
#endif
#if defined(__aarch64__)

View File

@@ -437,6 +437,7 @@ typedef struct PHY_VARS_gNB_s {
time_stats_t dlsch_rate_matching_stats;
time_stats_t dlsch_interleaving_stats;
time_stats_t dlsch_segmentation_stats;
time_stats_t dlsch_crc_stats;
time_stats_t dci_generation_stats;
time_stats_t phase_comp_stats;

View File

@@ -14,6 +14,7 @@
#include "common/utils/LOG/log.h"
#include "PHY/INIT/nr_phy_init.h"
#include "PHY/MODULATION/nr_modulation.h"
#include "PHY/NR_REFSIG/ss_pbch_nr.h"
#include "T.h"
#include "T_messages_creator.h"
#include "executables/nr-softmodem.h"
@@ -39,6 +40,90 @@ static void nr_fill_indication(const PHY_VARS_gNB *gNB,
nfapi_nr_crc_t *crc,
nfapi_nr_rx_data_pdu_t *pdu);
static inline bool prb_is_rotated(const uint64_t *prb_mask, int prb)
{
return (prb_mask[prb >> 6] >> (prb & 63)) & 0x1;
}
static inline void mark_prb(uint64_t *prb_mask, int prb_mask_words, int symbol, int prb)
{
uint64_t *symbol_mask = prb_mask + symbol * prb_mask_words;
symbol_mask[prb >> 6] |= UINT64_C(1) << (prb & 63);
}
static inline void mark_prb_range(uint64_t *prb_mask, int prb_mask_words, int symbol, int first_prb, int nb_prb)
{
for (int prb = first_prb; prb < first_prb + nb_prb; prb++)
mark_prb(prb_mask, prb_mask_words, symbol, prb);
}
static inline void apply_nr_rotation_TX_segment(const NR_DL_FRAME_PARMS *fp,
c16_t *txdataF,
const c16_t *symbol_rotation,
int slot,
int symbol,
int first_prb,
int nb_prb)
{
const int symb_offset = (slot % fp->slots_per_subframe) * fp->symbols_per_slot;
const c16_t *rot = &symbol_rotation[symb_offset + symbol];
c16_t *this_symbol = txdataF + symbol * fp->ofdm_symbol_size + first_prb * NR_NB_SC_PER_RB;
rotate_cpx_vector(this_symbol, rot, this_symbol, nb_prb * NR_NB_SC_PER_RB, 15);
}
static void merge_prb_mask(const NR_DL_FRAME_PARMS *fp,
const uint64_t *local_prb_mask,
uint64_t *global_prb_mask,
int prb_mask_words)
{
for (int symbol = 0; symbol < fp->symbols_per_slot; symbol++) {
const uint64_t *local_symbol_mask = local_prb_mask + symbol * prb_mask_words;
uint64_t *global_symbol_mask = global_prb_mask + symbol * prb_mask_words;
int prb = 0;
while (prb < fp->N_RB_DL) {
while (prb < fp->N_RB_DL && !prb_is_rotated(local_symbol_mask, prb))
prb++;
const int start_prb = prb;
while (prb < fp->N_RB_DL && prb_is_rotated(local_symbol_mask, prb))
prb++;
if (prb > start_prb) {
for (int rr = start_prb; rr < prb; rr++)
global_symbol_mask[rr >> 6] |= UINT64_C(1) << (rr & 63);
}
}
}
}
static void apply_nr_rotation_TX_masked(const NR_DL_FRAME_PARMS *fp,
c16_t **txdataF,
int num_tx_buffers,
const c16_t *symbol_rotation,
int slot,
const uint64_t *local_prb_mask,
uint64_t *global_prb_mask,
int prb_mask_words)
{
for (int aa = 0; aa < num_tx_buffers; aa++) {
if (!txdataF[aa])
continue;
for (int symbol = 0; symbol < fp->symbols_per_slot; symbol++) {
const uint64_t *local_symbol_mask = local_prb_mask + symbol * prb_mask_words;
int prb = 0;
while (prb < fp->N_RB_DL) {
while (prb < fp->N_RB_DL && !prb_is_rotated(local_symbol_mask, prb))
prb++;
const int start_prb = prb;
while (prb < fp->N_RB_DL && prb_is_rotated(local_symbol_mask, prb))
prb++;
if (prb > start_prb)
apply_nr_rotation_TX_segment(fp, txdataF[aa], symbol_rotation, slot, symbol, start_prb, prb - start_prb);
}
}
}
merge_prb_mask(fp, local_prb_mask, global_prb_mask, prb_mask_words);
}
void beam_index_allocation(uint16_t fapi_beam_index,
int ant,
int num_ports,
@@ -70,7 +155,12 @@ uint16_t get_first_ant_idx(bool das, uint16_t num_ports_beams, uint16_t beam_id,
return ((das) ? (beam_id & 0x7fff) * num_ports_beams : fapi_start_port);
}
void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu)
void nr_common_signal_procedures(PHY_VARS_gNB *gNB,
int frame,
int slot,
const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu,
uint64_t *phase_comp_prb_mask,
int prb_mask_words)
{
NR_DL_FRAME_PARMS *fp = &gNB->frame_parms;
const nfapi_nr_dl_tti_ssb_pdu_rel15_t *pdu = &ssb_pdu->ssb_pdu_rel15;
@@ -149,6 +239,26 @@ void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, const n
#endif
nr_generate_pbch(gNB, ssb_pdu, txdataF[ant_port], ssb_start_symbol, n_hf, frame, cfg, fp);
if (!gNB->phase_comp)
return;
uint64_t local_phase_comp_prb_mask[fp->symbols_per_slot][prb_mask_words];
memset(local_phase_comp_prb_mask, 0, sizeof(local_phase_comp_prb_mask));
const int ssb_start_prb = fp->ssb_start_subcarrier / NR_NB_SC_PER_RB;
const int ssb_nb_prb = (fp->ssb_start_subcarrier % NR_NB_SC_PER_RB + 240 + NR_NB_SC_PER_RB - 1) / NR_NB_SC_PER_RB;
for (int symbol = ssb_start_symbol; symbol < ssb_start_symbol + NR_N_SYMBOLS_SSB; symbol++)
mark_prb_range(&local_phase_comp_prb_mask[0][0], prb_mask_words, symbol, ssb_start_prb, ssb_nb_prb);
c16_t *ssb_txdataF[] = {txdataF[0]};
apply_nr_rotation_TX_masked(fp,
ssb_txdataF,
1,
fp->symbol_rotation[0],
slot,
&local_phase_comp_prb_mask[0][0],
phase_comp_prb_mask,
prb_mask_words);
}
// clearing beam information to be provided to RU for all slots (DL and UL)
@@ -163,7 +273,11 @@ void clear_slot_beamid(PHY_VARS_gNB *gNB, int slot)
}
}
static void nr_generate_csi_rs_gNB(PHY_VARS_gNB *gNB, int slot, const nfapi_nr_dl_tti_csi_rs_pdu *csi_rs_pdu)
static void nr_generate_csi_rs_gNB(PHY_VARS_gNB *gNB,
int slot,
const nfapi_nr_dl_tti_csi_rs_pdu *csi_rs_pdu,
uint64_t *phase_comp_prb_mask,
int prb_mask_words)
{
const nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *csi_params = &csi_rs_pdu->csi_rs_pdu_rel15;
if (csi_params->csi_type == 2) // ZP-CSI
@@ -213,6 +327,60 @@ static void nr_generate_csi_rs_gNB(PHY_VARS_gNB *gNB, int slot, const nfapi_nr_d
csi_params->power_control_offset_ss,
csi_params->cdm_type,
gNB->common_vars.txdataF + ant_port_offset);
if (!gNB->phase_comp)
return;
uint64_t local_phase_comp_prb_mask[gNB->frame_parms.symbols_per_slot][prb_mask_words];
memset(local_phase_comp_prb_mask, 0, sizeof(local_phase_comp_prb_mask));
for (int rb = csi_params->start_rb; rb < csi_params->start_rb + csi_params->nr_of_rbs; rb++) {
if ((csi_params->freq_density <= 1) && (csi_params->freq_density != (rb % 2)))
continue;
for (int j = 0; j < mapping_parms.size; j++) {
for (int lp = 0; lp <= mapping_parms.lprime; lp++) {
const int symbol = mapping_parms.loverline[j] + lp;
mark_prb(&local_phase_comp_prb_mask[0][0], prb_mask_words, symbol, rb);
}
}
}
apply_nr_rotation_TX_masked(&gNB->frame_parms,
gNB->common_vars.txdataF,
min(mapping_parms.ports, gNB->gNB_config.carrier_config.num_tx_ant.value),
gNB->frame_parms.symbol_rotation[0],
slot,
&local_phase_comp_prb_mask[0][0],
phase_comp_prb_mask,
prb_mask_words);
}
static void nr_generate_prs_gNB(PHY_VARS_gNB *gNB,
int slot,
int slot_prs,
prs_config_t *prs_config,
uint64_t *phase_comp_prb_mask,
int prb_mask_words)
{
const NR_DL_FRAME_PARMS *fp = &gNB->frame_parms;
nr_generate_prs(slot_prs, gNB->common_vars.txdataF[0], AMP, prs_config, fp);
if (!gNB->phase_comp)
return;
uint64_t local_phase_comp_prb_mask[fp->symbols_per_slot][prb_mask_words];
memset(local_phase_comp_prb_mask, 0, sizeof(local_phase_comp_prb_mask));
for (int symbol = prs_config->SymbolStart; symbol < prs_config->SymbolStart + prs_config->NumPRSSymbols; symbol++)
mark_prb_range(&local_phase_comp_prb_mask[0][0], prb_mask_words, symbol, prs_config->RBOffset, prs_config->NumRB);
c16_t *prs_txdataF[] = {gNB->common_vars.txdataF[0]};
apply_nr_rotation_TX_masked(fp,
prs_txdataF,
1,
fp->symbol_rotation[0],
slot,
&local_phase_comp_prb_mask[0][0],
phase_comp_prb_mask,
prb_mask_words);
}
void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
@@ -228,6 +396,10 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
if ((cfg->cell_config.frame_duplex_type.value == TDD) && (nr_slot_select(cfg,frame,slot) == NR_UPLINK_SLOT))
return;
const int prb_mask_words = (fp->N_RB_DL + 63) >> 6;
uint64_t phase_comp_prb_mask[gNB->common_vars.num_beams_period][fp->symbols_per_slot][prb_mask_words];
memset(phase_comp_prb_mask, 0, sizeof(phase_comp_prb_mask));
// clear the transmit data array and beam index for the current slot
for (int aa = 0; aa < fp->nb_antennas_tx; aa++) {
memset(gNB->common_vars.txdataF[aa], 0, fp->samples_per_slot_wCP * sizeof(**gNB->common_vars.txdataF));
@@ -243,26 +415,41 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
{
int slot_prs = (slot - i * prs_config->PRSResourceTimeGap + fp->slots_per_frame) % fp->slots_per_frame;
LOG_D(PHY,"gNB_TX: frame %d, slot %d, slot_prs %d, PRS Resource ID %d\n",frame, slot, slot_prs, rsc_id);
nr_generate_prs(slot_prs, gNB->common_vars.txdataF[0], AMP, prs_config, fp);
nr_generate_prs_gNB(gNB, slot, slot_prs, prs_config, &phase_comp_prb_mask[0][0][0], prb_mask_words);
}
}
}
for (int i = 0; i < UL_dci_req->numPdus; ++i)
nr_generate_dci(gNB, &UL_dci_req->ul_dci_pdu_list[i].pdcch_pdu.pdcch_pdu_rel15, &gNB->frame_parms, slot);
nr_generate_dci(gNB,
&UL_dci_req->ul_dci_pdu_list[i].pdcch_pdu.pdcch_pdu_rel15,
&gNB->frame_parms,
slot,
&phase_comp_prb_mask[0][0][0],
prb_mask_words);
int num_pdsch = 0;
for (int i = 0; i < DL_req->dl_tti_request_body.nPDUs; ++i) {
const nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdu = &DL_req->dl_tti_request_body.dl_tti_pdu_list[i];
switch (dl_tti_pdu->PDUType) {
case NFAPI_NR_DL_TTI_SSB_PDU_TYPE:
nr_common_signal_procedures(gNB, frame, slot, &dl_tti_pdu->ssb_pdu);
nr_common_signal_procedures(gNB,
frame,
slot,
&dl_tti_pdu->ssb_pdu,
&phase_comp_prb_mask[0][0][0],
prb_mask_words);
break;
case NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE:
nr_generate_dci(gNB, &dl_tti_pdu->pdcch_pdu.pdcch_pdu_rel15, &gNB->frame_parms, slot);
nr_generate_dci(gNB,
&dl_tti_pdu->pdcch_pdu.pdcch_pdu_rel15,
&gNB->frame_parms,
slot,
&phase_comp_prb_mask[0][0][0],
prb_mask_words);
break;
case NFAPI_NR_DL_TTI_CSI_RS_PDU_TYPE:
nr_generate_csi_rs_gNB(gNB, slot, &dl_tti_pdu->csi_rs_pdu);
nr_generate_csi_rs_gNB(gNB, slot, &dl_tti_pdu->csi_rs_pdu, &phase_comp_prb_mask[0][0][0], prb_mask_words);
break;
case NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE: {
int tx_data_idx = dl_tti_pdu->pdsch_pdu.pdsch_pdu_rel15.pduIndex;
@@ -287,28 +474,17 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
if (num_pdsch > 0) {
LOG_D(PHY, "PDSCH generation started (%d) in frame %d.%d\n", num_pdsch, frame, slot);
nr_generate_pdsch(gNB, num_pdsch, gNB->dlsch, frame, slot);
nr_generate_pdsch(gNB, num_pdsch, gNB->dlsch, frame, slot, &phase_comp_prb_mask[0][0][0], prb_mask_words);
}
//apply the OFDM symbol rotation here
start_meas(&gNB->phase_comp_stats);
for (int aa = 0; aa < fp->nb_antennas_tx; aa++) {
if (gNB->phase_comp) {
apply_nr_rotation_TX(fp,
gNB->common_vars.txdataF[aa],
true,
fp->symbol_rotation[0],
slot,
fp->N_RB_DL,
0,
fp->Ncp == NR_EXTENDED ? 12 : 14);
}
T(T_GNB_PHY_DL_OUTPUT_SIGNAL,
T_INT(0),
T_INT(frame),
T_INT(slot),
T_INT(aa),
T_BUFFER(gNB->common_vars.txdataF[aa], fp->samples_per_slot_wCP * sizeof(int32_t)));
for (int aa = 0; aa < cfg->carrier_config.num_tx_ant.value; aa++) {
T(T_GNB_PHY_DL_OUTPUT_SIGNAL,
T_INT(0),
T_INT(frame),
T_INT(slot),
T_INT(aa),
T_BUFFER(gNB->common_vars.txdataF[aa], fp->samples_per_slot_wCP * sizeof(int32_t)));
}
stop_meas(&gNB->phase_comp_stats);
}

View File

@@ -19,7 +19,12 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
void nr_save_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti_req);
int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, NR_UL_IND_t *UL_INFO);
void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, prach_item_t *prach_id, nfapi_nr_rach_indication_t *rach_ind);
void nr_common_signal_procedures (PHY_VARS_gNB *gNB,int frame,int slot, const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu);
void nr_common_signal_procedures(PHY_VARS_gNB *gNB,
int frame,
int slot,
const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu,
uint64_t *phase_comp_prb_mask,
int prb_mask_words);
void nr_feptx_ofdm(RU_t *ru,int frame_tx,int tti_tx);
void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa);
void nr_feptx_prec(RU_t *ru,int frame_tx,int tti_tx);

View File

@@ -745,7 +745,6 @@ int main(int argc, char **argv)
AssertFatal((gNB->if_inst = NR_IF_Module_init(0)) != NULL, "Cannot register interface");
gNB->if_inst->NR_PHY_config_req = nr_phy_config_request;
gNB->num_pdsch_symbols_per_thread = num_pdsch_symbols_per_thread;
NR_ServingCellConfigCommon_t *scc = calloc(1,sizeof(*scc));;
prepare_scc(scc);
@@ -849,6 +848,7 @@ int main(int argc, char **argv)
// nr_mac_config_scc()
gNB_mac->pre_processor_dl = nr_dlsim_preprocessor;
phy_init_nr_gNB(gNB);
gNB->num_pdsch_symbols_per_thread = num_pdsch_symbols_per_thread;
N_RB_DL = gNB->frame_parms.N_RB_DL;
NR_UE_info_t *UE_info = RC.nrmac[0]->UE_info.connected_ue_list[0];
@@ -1046,6 +1046,7 @@ int main(int argc, char **argv)
reset_meas(&gNB->dlsch_interleaving_stats);
reset_meas(&gNB->dlsch_rate_matching_stats);
reset_meas(&gNB->dlsch_segmentation_stats);
reset_meas(&gNB->dlsch_crc_stats);
reset_meas(&gNB->dlsch_modulation_stats);
reset_meas(&gNB->dlsch_pdsch_generation_stats);
reset_meas(&gNB->dlsch_precoding_stats);
@@ -1440,6 +1441,7 @@ int main(int argc, char **argv)
printDistribution(&gNB->phy_proc_tx,table_tx,"PHY proc tx");
printStatIndent2(&gNB->dci_generation_stats, "DCI encoding time");
printStatIndent2(&gNB->dlsch_encoding_stats,"DLSCH encoding time");
printStatIndent3(&gNB->dlsch_crc_stats,"DLSCH Outer CRC time");
printStatIndent3(&gNB->dlsch_segmentation_stats,"DLSCH segmentation time");
printStatIndent3(&gNB->tinput,"DLSCH LDPC input processing time");
printStatIndent3(&gNB->tprep,"DLSCH LDPC input preparation time");
@@ -1447,16 +1449,21 @@ int main(int argc, char **argv)
printStatIndent3(&gNB->toutput,"DLSCH LDPC output generation time");
printStatIndent3(&gNB->dlsch_rate_matching_stats,"DLSCH Rate Matching time");
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_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");
printStatIndent3(&gNB->dlsch_scrambling_stats, "DLSCH scrambling time");
if (gNB->dlsch_layer_mapping_stats.trials > 0) {
printStatIndent3(&gNB->dlsch_modulation_stats,"DLSCH modulation time");
printStatIndent3(&gNB->dlsch_layer_mapping_stats,"DLSCH Layer Mapping time");
}
else {
printStatIndent3(&gNB->dlsch_modulation_stats,"DLSCH mod/lm time");
}
if (num_pdsch_symbols_per_thread == 0) {
gNB->dlsch_resource_mapping_stats.trials = gNB->dlsch_modulation_stats.trials;
if (gNB->dlsch_precoding_stats.trials > 0) printStatIndent3(&gNB->dlsch_resource_mapping_stats,"DLSCH Resource Mapping time");
gNB->dlsch_precoding_stats.trials = gNB->dlsch_modulation_stats.trials;
if (gNB->dlsch_precoding_stats.trials > 0) printStatIndent3(&gNB->dlsch_precoding_stats,"DLSCH Precoding time");
}
if (use_cuda) {
printStatIndent(&pipeline_stats, "GPU Channel Pipeline");

View File

@@ -396,6 +396,7 @@ int main(int argc, char **argv)
RC.gNB[0] = malloc16_clear(sizeof(*(RC.gNB[0])));
gNB = RC.gNB[0];
gNB->ofdm_offset_divisor = UINT_MAX;
gNB->phase_comp = true;
frame_parms = &gNB->frame_parms; //to be initialized I suppose (maybe not necessary for PBCH)
frame_parms->nb_antennas_tx = n_tx;
frame_parms->nb_antennas_rx = n_rx;
@@ -489,6 +490,9 @@ int main(int argc, char **argv)
__attribute__ ((aligned(32))) c16_t rxdataF[UE->frame_parms.nb_antennas_rx][rxdataF_sz];
nfapi_nr_dl_tti_ssb_pdu ssb_pdu[64] = {0};
if (input_fd==NULL) {
const int prb_mask_words = (frame_parms->N_RB_DL + 63) / 64;
uint64_t phase_comp_prb_mask[gNB->common_vars.num_beams_period][frame_parms->symbols_per_slot][prb_mask_words];
memset(phase_comp_prb_mask, 0, sizeof(phase_comp_prb_mask));
for (i=0; i<frame_parms->Lmax; i++) {
if((SSB_positions >> i) & 0x01) {
@@ -506,22 +510,18 @@ int main(int argc, char **argv)
for (aa=0; aa<gNB->frame_parms.nb_antennas_tx; aa++)
memset(gNB->common_vars.txdataF[aa], 0, frame_parms->samples_per_slot_wCP * sizeof(int32_t));
nr_common_signal_procedures (gNB,frame,slot, &ssb_pdu[i]);
nr_common_signal_procedures(gNB,
frame,
slot,
&ssb_pdu[i],
&phase_comp_prb_mask[0][0][0],
prb_mask_words);
int samp = get_samples_slot_timestamp(frame_parms, slot);
for (aa = 0; aa < gNB->frame_parms.nb_antennas_tx; aa++) {
c16_t fft_in_buff[frame_parms->ofdm_symbol_size * frame_parms->symbols_per_slot] __attribute__((aligned(64)));
memset(fft_in_buff, 0, sizeof(fft_in_buff));
if (cyclic_prefix_type == 1) {
apply_nr_rotation_TX(frame_parms,
gNB->common_vars.txdataF[aa],
true,
frame_parms->symbol_rotation[0],
slot,
frame_parms->N_RB_DL,
0,
12);
fft_shift(gNB->common_vars.txdataF[aa],
frame_parms->ofdm_symbol_size,
frame_parms->N_RB_DL,
@@ -537,15 +537,6 @@ int main(int argc, char **argv)
frame_parms->nb_prefix_samples,
CYCLIC_PREFIX);
} else {
apply_nr_rotation_TX(frame_parms,
gNB->common_vars.txdataF[aa],
true,
frame_parms->symbol_rotation[0],
slot,
frame_parms->N_RB_DL,
0,
14);
fft_shift(gNB->common_vars.txdataF[aa],
frame_parms->ofdm_symbol_size,
frame_parms->N_RB_DL,