UE PRACH signal generation optimization
Use memcpy in nr_prach.c to fill txdata buffer. Remove global tables related to PRACH generation. Add new function to compute modulo multiplicative inverse and use it directly to get inverse instead of LUT. Removed nr_ru table and compute the values in-place. In UE, added new flag root_seq_computed in nrUE_config to only compute the sequence once after new nrUE_config is received.
This commit is contained in:
@@ -691,6 +691,7 @@ typedef struct
|
||||
fapi_nr_num_prach_fd_occasions_t* num_prach_fd_occasions_list;
|
||||
uint8_t ssb_per_rach;//SSB-per-RACH-occasion Value: 0: 1/8 1:1/4, 2:1/2 3:1 4:2 5:4, 6:8 7:16
|
||||
uint8_t prach_multiple_carriers_in_a_band;//0 = disabled 1 = enabled
|
||||
uint8_t root_seq_computed; // flag set and used only in PHY to indicate if table is computed with this config
|
||||
|
||||
} fapi_nr_prach_config_t;
|
||||
|
||||
|
||||
@@ -271,7 +271,6 @@ int init_nr_ue_signal(PHY_VARS_NR_UE *ue, int nb_connected_gNB)
|
||||
}
|
||||
|
||||
ue->init_averaging = 1;
|
||||
init_nr_prach_tables(839);
|
||||
init_symbol_rotation(fp);
|
||||
init_timeshift_rotation(fp);
|
||||
|
||||
|
||||
@@ -545,7 +545,8 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
|
||||
}
|
||||
} else { // This is the high-speed case
|
||||
new_dft = 0;
|
||||
nr_fill_du(N_ZC,prach_root_sequence_map);
|
||||
uint16_t nr_du[NR_PRACH_SEQ_LEN_L - 1];
|
||||
nr_fill_du(N_ZC, prach_root_sequence_map, nr_du);
|
||||
// set preamble_offset to initial rootSequenceIndex and look if we need more root sequences for this
|
||||
// preamble index and find the corresponding cyclic shift
|
||||
// Check if all shifts for that root have been processed
|
||||
|
||||
@@ -30,9 +30,6 @@
|
||||
* \warning
|
||||
*/
|
||||
|
||||
extern c16_t nr_ru[839]; // quantized roots of unity
|
||||
extern uint16_t nr_du[838];
|
||||
|
||||
static const char* const prachfmt[] = {"0", "1", "2", "3", "A1", "A2", "A3", "B1", "B4", "C0", "C2", "A1/B1", "A2/B2", "A3/B3"};
|
||||
|
||||
/*************************************
|
||||
|
||||
@@ -38,12 +38,34 @@
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "common/utils/LOG/vcd_signal_dumper.h"
|
||||
#include "T.h"
|
||||
c16_t nr_ru[839]; // quantized roots of unity
|
||||
static uint32_t nr_ZC_inv[839]; // multiplicative inverse for roots u
|
||||
uint16_t nr_du[838];
|
||||
|
||||
/* Extended Euclidean Algorithm to compute modulo inverse */
|
||||
static int modulo_multiplicative_inverse(const int a, const int m)
|
||||
{
|
||||
DevAssert(a < m);
|
||||
int t = 0;
|
||||
int newt = 1;
|
||||
int r = m;
|
||||
int newr = a;
|
||||
|
||||
while (newr != 0) {
|
||||
const int q = r / newr;
|
||||
int tmp = t;
|
||||
t = newt;
|
||||
newt = tmp - q * newt;
|
||||
tmp = r;
|
||||
r = newr;
|
||||
newr = tmp - q * newr;
|
||||
}
|
||||
|
||||
AssertFatal(r <= 1, "Modulo inverse doesn't exist for a %d, m %d\n", a, m);
|
||||
t = (t < 0) ? t + m : t;
|
||||
LOG_D(PHY, "Modulo %d inverse of %d is %d\n", m, a, t);
|
||||
return t;
|
||||
}
|
||||
|
||||
// This function computes the du
|
||||
void nr_fill_du(uint16_t N_ZC, const uint16_t *prach_root_sequence_map)
|
||||
void nr_fill_du(uint16_t N_ZC, const uint16_t* prach_root_sequence_map, uint16_t nr_du[NR_PRACH_SEQ_LEN_L - 1])
|
||||
{
|
||||
|
||||
uint16_t iu,u,p;
|
||||
@@ -64,19 +86,15 @@ void nr_fill_du(uint16_t N_ZC, const uint16_t *prach_root_sequence_map)
|
||||
void compute_nr_prach_seq(uint8_t short_sequence, uint8_t num_sequences, uint8_t rootSequenceIndex, c16_t X_u[64][839])
|
||||
{
|
||||
// Compute DFT of x_u => X_u[k] = x_u(inv(u)*k)^* X_u[k] = exp(j\pi u*inv(u)*k*(inv(u)*k+1)/N_ZC)
|
||||
unsigned int k,inv_u,i;
|
||||
int N_ZC;
|
||||
|
||||
const uint16_t *prach_root_sequence_map;
|
||||
uint16_t u;
|
||||
const uint16_t* prach_root_sequence_map;
|
||||
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_UE_COMPUTE_PRACH, VCD_FUNCTION_IN);
|
||||
|
||||
LOG_D(PHY,"compute_prach_seq: prach short sequence %x, num_sequences %d, rootSequenceIndex %d\n", short_sequence, num_sequences, rootSequenceIndex);
|
||||
|
||||
N_ZC = (short_sequence) ? 139 : 839;
|
||||
|
||||
init_nr_prach_tables(N_ZC);
|
||||
N_ZC = (short_sequence) ? NR_PRACH_SEQ_LEN_S : NR_PRACH_SEQ_LEN_L;
|
||||
|
||||
if (short_sequence) {
|
||||
// FIXME cannot be reached
|
||||
@@ -87,7 +105,7 @@ void compute_nr_prach_seq(uint8_t short_sequence, uint8_t num_sequences, uint8_t
|
||||
|
||||
LOG_D( PHY, "compute_prach_seq: done init prach_tables\n" );
|
||||
|
||||
for (i=0; i<num_sequences; i++) {
|
||||
for (int i = 0; i < num_sequences; i++) {
|
||||
int index = (rootSequenceIndex+i) % (N_ZC-1);
|
||||
|
||||
if (short_sequence) {
|
||||
@@ -98,58 +116,22 @@ void compute_nr_prach_seq(uint8_t short_sequence, uint8_t num_sequences, uint8_t
|
||||
DevAssert( index < sizeof(prach_root_sequence_map_0_3) / sizeof(prach_root_sequence_map_0_3[0]) );
|
||||
}
|
||||
|
||||
u = prach_root_sequence_map[index];
|
||||
const uint16_t u = prach_root_sequence_map[index];
|
||||
LOG_D(PHY,"prach index %d => u=%d\n",index,u);
|
||||
inv_u = nr_ZC_inv[u]; // multiplicative inverse of u
|
||||
|
||||
const int inv_u = modulo_multiplicative_inverse(u, N_ZC); // multiplicative inverse of u
|
||||
|
||||
// X_u[0] stores the first ZC sequence where the root u has a non-zero number of shifts
|
||||
// for the unrestricted case X_u[0] is the first root indicated by the rootSequenceIndex
|
||||
|
||||
for (k=0; k<N_ZC; k++) {
|
||||
const int zc_inv_2 = modulo_multiplicative_inverse(2, N_ZC);
|
||||
for (int k = 0; k < N_ZC; k++) {
|
||||
const unsigned int j = (((k * (1 + (inv_u * k))) % N_ZC) * zc_inv_2) % N_ZC;
|
||||
const double w = 2 * M_PI * (double)j / N_ZC;
|
||||
const c16_t ru = {.r = (int16_t)(floor(32767.0 * cos(w))), .i = (int16_t)(floor(32767.0 * sin(w)))};
|
||||
// multiply by inverse of 2 (required since ru is exp[j 2\pi n])
|
||||
X_u[i][k] = nr_ru[(((k * (1 + (inv_u * k))) % N_ZC) * nr_ZC_inv[2]) % N_ZC];
|
||||
X_u[i][k] = ru;
|
||||
}
|
||||
}
|
||||
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_UE_COMPUTE_PRACH, VCD_FUNCTION_OUT);
|
||||
|
||||
}
|
||||
|
||||
void init_nr_prach_tables(int N_ZC)
|
||||
{
|
||||
|
||||
int i,m;
|
||||
|
||||
// Compute the modular multiplicative inverse 'iu' of u s.t. iu*u = 1 mod N_ZC
|
||||
nr_ZC_inv[0] = 0;
|
||||
nr_ZC_inv[1] = 1;
|
||||
|
||||
for (i=2; i<N_ZC; i++) {
|
||||
for (m=2; m<N_ZC; m++)
|
||||
if (((i*m)%N_ZC) == 1) {
|
||||
nr_ZC_inv[i] = m;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef PRACH_DEBUG
|
||||
|
||||
if (i<16)
|
||||
printf("i %d : inv %d\n",i,nr_ZC_inv[i]);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// Compute quantized roots of unity
|
||||
for (i=0; i<N_ZC; i++) {
|
||||
nr_ru[i].r = (int16_t)(floor(32767.0 * cos(2 * M_PI * (double)i / N_ZC)));
|
||||
nr_ru[i].i = (int16_t)(floor(32767.0 * sin(2 * M_PI * (double)i / N_ZC)));
|
||||
#ifdef PRACH_DEBUG
|
||||
|
||||
if (i<16)
|
||||
printf("i %d : runity %d,%d\n", i, nr_ru[i].r, nr_ru[i].i);
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,9 +67,7 @@ uint32_t nr_get_E(uint32_t G, uint8_t C, uint8_t Qm, uint8_t Nl, uint8_t r);
|
||||
|
||||
void compute_nr_prach_seq(uint8_t short_sequence, uint8_t num_sequences, uint8_t rootSequenceIndex, c16_t X_u[64][839]);
|
||||
|
||||
void nr_fill_du(uint16_t N_ZC, const uint16_t *prach_root_sequence_map);
|
||||
|
||||
void init_nr_prach_tables(int N_ZC);
|
||||
void nr_fill_du(uint16_t N_ZC, const uint16_t *prach_root_sequence_map, uint16_t nr_du[NR_PRACH_SEQ_LEN_L - 1]);
|
||||
|
||||
void nr_codeword_scrambling(uint8_t *in,
|
||||
uint32_t size,
|
||||
@@ -84,4 +82,5 @@ void nr_codeword_unscrambling_init(int16_t *s, uint32_t size, uint8_t q, uint32_
|
||||
/**@}*/
|
||||
|
||||
void init_pucch2_luts(void);
|
||||
void set_prach_tables(int N_ZC, c16_t** ru, uint32_t** zc_inv);
|
||||
#endif
|
||||
|
||||
@@ -63,17 +63,17 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
|
||||
const uint16_t *prach_root_sequence_map;
|
||||
uint16_t preamble_shift = 0, preamble_index0, n_shift_ra, n_shift_ra_bar, d_start=INT16_MAX, numshift, N_ZC, u, offset, offset2, first_nonzero_root_idx;
|
||||
c16_t prach[(4688 + 4 * 24576) * 2] __attribute__((aligned(32))) = {0};
|
||||
int16_t prachF_tmp[(4688+4*24576)*4*2] __attribute__((aligned(32))) = {0};
|
||||
c16_t prachF_tmp[(4688 + 4 * 24576) * 4] __attribute__((aligned(32))) = {0};
|
||||
|
||||
int Ncp = 0;
|
||||
int prach_start, prach_sequence_length, i, prach_len, dftlen, mu, kbar, K, n_ra_prb, k, prachStartSymbol, sample_offset_slot;
|
||||
int prach_start, prach_sequence_length, prach_len, dftlen, mu, kbar, K, n_ra_prb, k, prachStartSymbol, sample_offset_slot;
|
||||
|
||||
fd_occasion = 0;
|
||||
prach_len = 0;
|
||||
dftlen = 0;
|
||||
first_nonzero_root_idx = 0;
|
||||
int16_t amp = prach_pdu->prach_tx_power;
|
||||
int16_t *prachF = prachF_tmp;
|
||||
c16_t *prachF = prachF_tmp;
|
||||
Mod_id = ue->Mod_id;
|
||||
prach_sequence_length = nrUE_config->prach_config.prach_sequence_length;
|
||||
N_ZC = (prach_sequence_length == 0) ? 839:139;
|
||||
@@ -91,10 +91,13 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
|
||||
|
||||
LOG_D(PHY,"Generate NR PRACH %d.%d\n", frame, slot);
|
||||
|
||||
compute_nr_prach_seq(nrUE_config->prach_config.prach_sequence_length,
|
||||
nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].num_root_sequences,
|
||||
nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].prach_root_sequence_index,
|
||||
ue->X_u);
|
||||
if (nrUE_config->prach_config.root_seq_computed == 0) {
|
||||
compute_nr_prach_seq(nrUE_config->prach_config.prach_sequence_length,
|
||||
nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].num_root_sequences,
|
||||
nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].prach_root_sequence_index,
|
||||
ue->X_u);
|
||||
nrUE_config->prach_config.root_seq_computed = 1;
|
||||
}
|
||||
|
||||
if (prachStartSymbol == 0) {
|
||||
sample_offset_slot = 0;
|
||||
@@ -137,7 +140,8 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
|
||||
#endif
|
||||
|
||||
not_found = 1;
|
||||
nr_fill_du(N_ZC,prach_root_sequence_map);
|
||||
uint16_t nr_du[NR_PRACH_SEQ_LEN_L - 1];
|
||||
nr_fill_du(N_ZC, prach_root_sequence_map, nr_du);
|
||||
preamble_index0 = preamble_index;
|
||||
// set preamble_offset to initial rootSequenceIndex and look if we need more root sequences for this
|
||||
// preamble index and find the corresponding cyclic shift
|
||||
@@ -223,7 +227,6 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
|
||||
|
||||
k *= K;
|
||||
k += kbar;
|
||||
k *= 2;
|
||||
|
||||
LOG_I(PHY,
|
||||
"PRACH [UE %d] in frame.slot %d.%d, placing PRACH in position %d, Msg1/MsgA-Preamble frequency start %d (k1 %d), "
|
||||
@@ -231,7 +234,7 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
|
||||
Mod_id,
|
||||
frame,
|
||||
slot,
|
||||
k,
|
||||
k * 2,
|
||||
n_ra_prb,
|
||||
nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].k1,
|
||||
preamble_offset,
|
||||
@@ -413,12 +416,14 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
|
||||
|
||||
if (offset2 >= N_ZC)
|
||||
offset2 -= N_ZC;
|
||||
const int32_t Xu_re = (Xu[offset].r * amp) >> 15;
|
||||
const int32_t Xu_im = (Xu[offset].i * amp) >> 15;
|
||||
prachF[k++] = (Xu_re * nr_ru[offset2].r - Xu_im * nr_ru[offset2].i) >> 15;
|
||||
prachF[k++] = (Xu_im * nr_ru[offset2].r + Xu_re * nr_ru[offset2].i) >> 15;
|
||||
const c16_t Xu_t = c16xmulConstShift(Xu[offset], amp, 15);
|
||||
const double w = 2 * M_PI * (double)offset2 / N_ZC;
|
||||
const c16_t ru = {.r = (int16_t)(floor(32767.0 * cos(w))), .i = (int16_t)(floor(32767.0 * sin(w)))};
|
||||
const c16_t p = c16mulShift(Xu_t, ru, 15);
|
||||
prachF[k++] = p;
|
||||
|
||||
if (k==dftlen) k=0;
|
||||
if (k * 2 == dftlen)
|
||||
k = 0;
|
||||
}
|
||||
|
||||
#if defined (PRACH_WRITE_OUTPUT_DEBUG)
|
||||
@@ -429,7 +434,7 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
|
||||
// This is after cyclic prefix
|
||||
c16_t *prach2 = prach + Ncp;
|
||||
const idft_size_idx_t idft_size = get_idft(dftlen);
|
||||
idft(idft_size, prachF, (int16_t *)prach, 1);
|
||||
idft(idft_size, (int16_t *)prachF, (int16_t *)prach, 1);
|
||||
memmove(prach2, prach, (dftlen << 2));
|
||||
|
||||
if (prach_sequence_length == 0) {
|
||||
@@ -504,21 +509,17 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef NR_PRACH_DEBUG
|
||||
LOG_I(PHY, "PRACH [UE %d] N_RB_UL %d prach_start %d, prach_len %d\n", Mod_id,
|
||||
fp->N_RB_UL,
|
||||
prach_start,
|
||||
prach_len);
|
||||
#endif
|
||||
#ifdef NR_PRACH_DEBUG
|
||||
LOG_I(PHY, "PRACH [UE %d] N_RB_UL %d prach_start %d, prach_len %d\n", Mod_id, fp->N_RB_UL, prach_start, prach_len);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < prach_len; i++)
|
||||
ue->common_vars.txData[0][prach_start + i] = prach[i];
|
||||
memcpy(ue->common_vars.txData[0] + prach_start, prach, sizeof(c16_t) * prach_len);
|
||||
|
||||
#ifdef PRACH_WRITE_OUTPUT_DEBUG
|
||||
LOG_M("prach_tx0.m", "prachtx0", prach+(Ncp<<1), prach_len-Ncp, 1, 1);
|
||||
LOG_M("Prach_txsig.m","txs",(int16_t*)(&ue->common_vars.txdata[0][prach_start]), 2*(prach_start+prach_len), 1, 1)
|
||||
#endif
|
||||
LOG_M("prach_tx0.m", "prachtx0", prach + (Ncp << 1), prach_len - Ncp, 1, 1);
|
||||
LOG_M("Prach_txsig.m", "txs", (int16_t *)(&ue->common_vars.txdata[0][prach_start]), 2 * (prach_start + prach_len), 1, 1)
|
||||
#endif
|
||||
|
||||
return signal_energy((int*)prach, 256);
|
||||
return signal_energy((int *)prach, 256);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,8 @@ static const uint8_t N_slot_subframe[MU_NUMBER] = {1, 2, 4, 8, 16};
|
||||
|
||||
#define NB_DL_DATA_TO_UL_ACK (8) /* size of table TS 38.213 Table 9.2.3-1 */
|
||||
|
||||
#define NR_PRACH_SEQ_LEN_L 839
|
||||
#define NR_PRACH_SEQ_LEN_S 139
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user