Files
openairinterface5g/radio/fhi_72/oaioran.c
Teodora Vladić f1c8e7d3f1 WIP: Remove PUxCH/SRS queue
NR_gNB_PUCCH_job_t and NR_gNB_PUSCH_job_t which are stored in pucch_queue and pusch_queue and do not contain the information we need for fronthaul: rxdataF, and beam_id. All is stored in RU_t and can be obtained via
  RU_t *ru = RC.ru[0];
  PHY_VARS_gNB *gNB = ru->gNB_list[0];which not ideal but does the job. The same lines are used in oai_xran_fh_rx_prach_callback(), except that prach_item_t (prach_buf, ant_start) is stored in prach_ru_queue and prach_l1rx_queue which allows extraction of the necessary data.

Therefore, I decided to not reuse the same template as for PRACH, i.e. to create an additional queues for PUCCH/PUSCH. So I added just a bool, at least it only needs 1 byte.
We shall assume that for any topology, other than split 7.2, PUCCH/PUSCH is always received in scheduled slots (which is also the current logic).
We shall assume that for split 7.2, PUCCH/PUSCH can only be received if scheduled -> scheduled and not received can happen if the ethernet packet is not properly filled, e.g. numRB = 0, but pData != NULL.

The reason we need this bool for 7.2 is that somehow we need to keep track between xran_thread and L1_tx_thread. For other topology, it shall keep track between ru_thread and L1_tx_thread.

TODO:

* delete oran_fh_if4p5_south_in and somehow align timing with RU_proc_t
* better handle get_current_* (PUxCH/SRS) jobs

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-06-05 18:12:36 +02:00

717 lines
31 KiB
C

/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdatomic.h>
#include "xran_fh_o_du.h"
#include "xran_compression.h"
#include "armral_bfp_compression.h"
#if defined(__arm__) || defined(__aarch64__)
#else
// xran_cp_api.h uses SIMD, but does not include it
#include <immintrin.h>
#endif
#include "xran_cp_api.h"
#include "xran_sync_api.h"
#include "oran_isolate.h"
#include "oran-init.h"
#include "oaioran.h"
#include <rte_ethdev.h>
#include "oran-config.h" // for g_kbar
#include "openair1/SCHED_NR/sched_nr.h"
#include "common/utils/threadPool/notified_fifo.h"
#include "common/utils/fsn.h"
#include "common/ran_context.h"
#define N_SC_PER_PRB 12
// Declare variable useful for the send buffer function
volatile bool first_call_set = false;
int xran_is_prach_slot(uint8_t PortId, uint32_t subframe_id, uint32_t slot_id, uint8_t mu);
#include "common/utils/LOG/log.h"
atomic_int xran_queue_prach_length = 0;
extern notifiedFIFO_t oran_sync_fifo_prach;
/* Prints TX_TOTAL, RX_TOTAL, RX_ON_TIME, RX_ERR_DROP counters every 128 frames. */
void print_fhi_counters(ru_info_t *ru, const int frame, const int slot)
{
static int64_t old_rx_counter[XRAN_PORTS_NUM] = {0};
static int64_t old_tx_counter[XRAN_PORTS_NUM] = {0};
struct xran_common_counters x_counters[XRAN_PORTS_NUM];
const struct xran_fh_init *fh_init = get_xran_fh_init();
for (int o_xu_id = 0; o_xu_id < fh_init->xran_ports; o_xu_id++) {
if ((frame & 0x7f) == 0 && slot == 0 && xran_get_common_counters(gxran_handle[o_xu_id], &x_counters[o_xu_id]) == XRAN_STATUS_SUCCESS) {
LOG_I(HW,
"[%s%d][rx %7ld pps %7ld kbps %7ld][tx %7ld pps %7ld kbps %7ld][Total Msgs_Rcvd %ld]\n",
"o-du ",
o_xu_id,
x_counters[o_xu_id].rx_counter,
x_counters[o_xu_id].rx_counter - old_rx_counter[o_xu_id],
x_counters[o_xu_id].rx_bytes_per_sec * 8 / 1000L,
x_counters[o_xu_id].tx_counter,
x_counters[o_xu_id].tx_counter - old_tx_counter[o_xu_id],
x_counters[o_xu_id].tx_bytes_per_sec * 8 / 1000L,
x_counters[o_xu_id].Total_msgs_rcvd);
for (int rxant = 0; rxant < ru->nb_rx / fh_init->xran_ports; rxant++)
LOG_I(HW,
"[%s%d][pusch%d %7ld prach%d %7ld]\n",
"o_du",
o_xu_id,
rxant,
x_counters[o_xu_id].rx_pusch_packets[rxant],
rxant,
x_counters[o_xu_id].rx_prach_packets[rxant]);
LOG_I(HW,
"[%s%d][drop errors %7d ecpri errors %7d cp errors %7d up errors %7d pusch errors %7d prach errors %7d]\n",
"o_du",
o_xu_id,
x_counters[o_xu_id].rx_err_drop,
x_counters[o_xu_id].rx_err_ecpri,
x_counters[o_xu_id].rx_err_cp,
x_counters[o_xu_id].rx_err_up,
x_counters[o_xu_id].rx_err_pusch,
x_counters[o_xu_id].rx_err_prach);
if (x_counters[o_xu_id].rx_counter > old_rx_counter[o_xu_id])
old_rx_counter[o_xu_id] = x_counters[o_xu_id].rx_counter;
if (x_counters[o_xu_id].tx_counter > old_tx_counter[o_xu_id])
old_tx_counter[o_xu_id] = x_counters[o_xu_id].tx_counter;
}
}
}
/** @details Only used to unblock timing in oai_xran_fh_rx_callback()/oai_xran_fh_rx_prach_callback()
* on first call. */
int oai_physide_dl_tti_call_back(void *param, uint8_t mu)
{
if (!first_call_set) {
LOG_I(HW, "first_call set from phy cb\n");
}
first_call_set = true;
return 0;
}
/** @details Read SRS data from xran buffers for each RU (for each `port_id`).
* If I/Q compression (bitwidth < 16 bits) is configured, decompresses the data
* before writing. */
void oai_xran_fh_rx_srs_callback(void *pCallbackTag, xran_status_t status, uint8_t mu)
{
if (!first_call_set)
return;
struct xran_cb_tag *callback_tag = (struct xran_cb_tag *)pCallbackTag;
uint32_t port_id = callback_tag->oXuId;
struct xran_fh_config *fh_cfg = get_xran_fh_config(port_id);
uint32_t num_ant_elem = fh_cfg->nAntElmTRx;
const int slots_in_sf = 1 << mu;
const int sf_in_frame = 10;
uint32_t tti = callback_tag->slotiId;
uint32_t frame = XranGetFrameNum(tti, 0, sf_in_frame, slots_in_sf);
//uint32_t subframe = XranGetSubFrameNum(tti, slots_in_sf, sf_in_frame);
uint32_t slot = XranGetSlotNum(tti, slots_in_sf * sf_in_frame); // slot within a frame, not a subframe
uint32_t rx_sym = callback_tag->symbol & 0xFF; // rx_sym = 7 => cb full slot
LOG_D(HW, "[%d.%d] %s, tti %d rx_sym %d ru_id %d\n", frame, slot, __FUNCTION__, tti, rx_sym, port_id);
// TODO: reset the number of received packets for non SRS slots
for (uint16_t cc_id = 0; cc_id < 1 /*nSectorNum*/; cc_id++) {
oran_buf_list_t *bufs = get_xran_buffers(port_id);
for (int ant_id = 0; ant_id < num_ant_elem; ant_id++) {
for (int sym_idx = 0; sym_idx < XRAN_NUM_OF_SYMBOL_PER_SLOT; sym_idx++) {
struct xran_prb_map *pPrbMap = (struct xran_prb_map *)bufs->srsdstdecomp[ant_id][tti % XRAN_N_FE_BUF_LEN].pBuffers->pData;
pPrbMap->sFrontHaulRxPacketCtrl[sym_idx].nRxPkt = 0;
}
}
}
// TODO: map SRS IQ data into OAI (new buffer or reuse rxdataF ?)
}
/** @details Read PRACH data from xran buffers for each RU (for each `port_id`).
* If I/Q compression (bitwidth < 16 bits) is configured, decompresses the data
* before writing. */
void oai_xran_fh_rx_prach_callback(void *pCallbackTag, xran_status_t status, uint8_t mu)
{
if (!first_call_set)
return;
struct xran_cb_tag *callback_tag = (struct xran_cb_tag *)pCallbackTag;
uint32_t port_id = callback_tag->oXuId;
struct xran_fh_config *fh_cfg = get_xran_fh_config(port_id);
int num_prach_ant = fh_cfg->neAxc;
const int slots_in_sf = 1 << mu;
const int sf_in_frame = 10;
uint32_t tti = callback_tag->slotiId;
uint32_t frame = XranGetFrameNum(tti, 0, sf_in_frame, slots_in_sf);
uint32_t subframe = XranGetSubFrameNum(tti, slots_in_sf, sf_in_frame);
uint32_t slot = XranGetSlotNum(tti, slots_in_sf * sf_in_frame); // slot within a frame, not a subframe
uint32_t rx_sym = callback_tag->symbol & 0xFF; // rx_sym = 7 => cb full slot
LOG_D(HW, "[%d.%d] %s, tti %d rx_sym %d ru_id %d\n", frame, slot, __FUNCTION__, tti, rx_sym, port_id);
nr_prach_info_t prach_info = get_prach_info(port_id);
prach_item_t p;
RU_t *ru = RC.ru[0];
PHY_VARS_gNB *gNB = ru->gNB_list[0];
fsn_t now = {.f = frame, .s = slot, .mu = mu};
if (get_next_nr_prach(&gNB->prach_ru_queue, &now, &p)) {
// PRACH occasion in a frame if and only if SFN % x == y, TS 38.211 Table 6.3.3.2-2/3/4
bool is_prach_frame = (frame % prach_info.x == prach_info.y);
bool is_prach_slot = is_prach_frame && xran_is_prach_slot(0, subframe, (p.slot % slots_in_sf), mu); // `p.slot` = slot in which PRACH is scheduled
if (!is_prach_slot) {
LOG_W(HW, "[%d.%d] Expected PRACH reception of scheduled slot %d\n", frame, slot, p.slot);
return;
}
} else {
// reset the number of received packets for slots that are not expected to be PRACH slots
for (uint16_t cc_id = 0; cc_id < 1 /*nSectorNum*/; cc_id++) {
oran_buf_list_t *bufs = get_xran_buffers(port_id);
for (int ant_id = 0; ant_id < num_prach_ant; ant_id++) {
for (int sym_idx = 0; sym_idx < XRAN_NUM_OF_SYMBOL_PER_SLOT; sym_idx++) {
struct xran_prb_map *pPrbMap = (struct xran_prb_map *)bufs->prachdstdecomp[ant_id][tti % XRAN_N_FE_BUF_LEN].pBuffers->pData;
pPrbMap->sFrontHaulRxPacketCtrl[sym_idx].nRxPkt = 0;
}
}
}
return;
}
struct xran_ru_config *ru_conf = &fh_cfg->ru_conf;
struct xran_prach_config *prach_conf = &fh_cfg->perMu[mu].prach_conf;
for (uint16_t cc_id = 0; cc_id < 1 /*nSectorNum*/; cc_id++) { // OAI does not support multiple CC yet.
oran_buf_list_t *bufs = get_xran_buffers(port_id);
for (int ant_id = p.ant_start; ant_id < p.ant_start + num_prach_ant; ant_id++) {
for (int sym_idx = prach_conf->startSymId; sym_idx <= prach_conf->lastSymId; sym_idx++) {
int16_t *dst, *src;
int idx = 0;
// hardcoded to use only first prach occasion
dst = (int16_t *)p.prach_buf[ant_id + (num_prach_ant * port_id) - p.ant_start][0];
struct xran_prb_map *pPrbMap = (struct xran_prb_map *)bufs->prachdstdecomp[ant_id - p.ant_start][tti % XRAN_N_FE_BUF_LEN].pBuffers->pData;
struct xran_rx_packet_ctl *p_rx_packet_ctl = &pPrbMap->sFrontHaulRxPacketCtrl[sym_idx];
int32_t nRxPkt = p_rx_packet_ctl->nRxPkt;
p_rx_packet_ctl->nRxPkt = 0; // for the next PRACH slot
LOG_D(HW, "[%d.%d] tti %d port_id %d ant_id %d sym_idx %d dst %p nRxPkt %d\n", frame, slot, tti, port_id, ant_id, sym_idx, dst, nRxPkt);
/* known issue: for ant_id = 2 (when num_prach_ant = 4), the packet (one per symbol) is not received/stored on time,
* so in the next PRACH occurence two packets were detected, instead of one */
if (nRxPkt != 1) {
LOG_D(HW, "[%d.%d] tti %d port_id %d ant_id %d sym_idx %d nRxPkt %d (expected 1 packet)\n", frame, slot, tti, port_id, ant_id, sym_idx, nRxPkt);
continue;
}
src = (int16_t *)p_rx_packet_ctl->pData[0];
if (src == NULL) { // protection
LOG_E(HW, "[%d.%d] tti %d port_id %d ant_id %d sym_idx %d src = NULL\n", frame, slot, tti, port_id, ant_id, sym_idx);
continue;
}
uint16_t num_prbu = p_rx_packet_ctl->nRBSize[0];
uint16_t N_ZC = ((prach_info.format & 0xff) < 4) ? 839 : 139;
/* convert Network order to host order */
if (ru_conf->compMeth_PRACH == XRAN_COMPMETHOD_NONE) {
if (sym_idx == prach_conf->startSymId) {
for (idx = 0; idx < N_ZC * 2; idx++) {
dst[idx] = ((int16_t)ntohs(src[idx + g_kbar]));
}
} else {
for (idx = 0; idx < N_ZC * 2; idx++) {
dst[idx] += ((int16_t)ntohs(src[idx + g_kbar]));
}
}
} else if (ru_conf->compMeth_PRACH == XRAN_COMPMETHOD_BLKFLOAT) {
int16_t local_dst[num_prbu * 2 * N_SC_PER_PRB] __attribute__((aligned(64)));
#if defined(__i386__) || defined(__x86_64__)
struct xranlib_decompress_request bfp_decom_req = {};
struct xranlib_decompress_response bfp_decom_rsp = {};
int payload_len = (3 * ru_conf->iqWidth_PRACH + 1) * num_prbu;
bfp_decom_req.data_in = (int8_t *)src;
bfp_decom_req.numRBs = num_prbu;
bfp_decom_req.len = payload_len;
bfp_decom_req.compMethod = XRAN_COMPMETHOD_BLKFLOAT;
bfp_decom_req.iqWidth = ru_conf->iqWidth_PRACH;
bfp_decom_rsp.data_out = (int16_t *)local_dst;
bfp_decom_rsp.len = 0;
xranlib_decompress_avx512(&bfp_decom_req, &bfp_decom_rsp);
#elif defined(__arm__) || defined(__aarch64__)
armral_bfp_decompression(ru_conf->iqWidth_PRACH, num_prbu, (int8_t *)src, (int16_t *)local_dst);
#else
AssertFatal(1 == 0, "BFP decompression not supported on this architecture");
#endif
if (sym_idx == prach_conf->startSymId)
for (idx = 0; idx < (N_ZC * 2); idx++)
dst[idx] = local_dst[idx + g_kbar];
else
for (idx = 0; idx < (N_ZC * 2); idx++)
dst[idx] += (local_dst[idx + g_kbar]);
} // COMPMETHOD_BLKFLOAT
} // sym_idx
} // ant_id
} // cc_id
// after reading PRACH, write back to queue
bool success = spsc_q_put(&gNB->prach_l1rx_queue, &p, sizeof(p));
// assume prach_l1rx_queue never full: prach_ru_queue filled at
// constant pace, but prach_l1rx_queue emptied as fast as possible,
// see rx_func()
DevAssert(success);
}
/** @brief Check if symbol in slot is UL.
*
* @param frame_conf xran frame configuration
* @param slot the current (absolute) slot (number)
* @param sym_idx the current symbol index */
static bool is_tdd_ul_symbol(const struct xran_frame_config *frame_conf, int slot, int sym_idx)
{
/* in FDD, every symbol is also UL */
if (frame_conf->nFrameDuplexType == XRAN_FDD)
return true;
int tdd_period = frame_conf->nTddPeriod;
int slot_in_period = slot % tdd_period;
/* check if symbol is UL */
return frame_conf->sSlotConfig[slot_in_period].nSymbolType[sym_idx] == 1 /* UL */;
}
/** @brief Check if symbol in slot is DL.
*
* @param frame_conf xran frame configuration
* @param slot the current (absolute) slot (number)
* @param sym_idx the current symbol index */
static bool is_tdd_dl_symbol(const struct xran_frame_config *frame_conf, int slot, int sym_idx)
{
/* in FDD, every symbol is also UL */
if (frame_conf->nFrameDuplexType == XRAN_FDD)
return true;
int tdd_period = frame_conf->nTddPeriod;
int slot_in_period = slot % tdd_period;
/* check if symbol is UL */
return frame_conf->sSlotConfig[slot_in_period].nSymbolType[sym_idx] == 0 /* DL */;
}
/** @brief Check if current slot is guard/mixed */
static bool is_tdd_guard_slot(const struct xran_frame_config *frame_conf, int slot)
{
return (is_tdd_dl_symbol(frame_conf, slot, 0) && is_tdd_ul_symbol(frame_conf, slot, XRAN_NUM_OF_SYMBOL_PER_SLOT - 1));
}
/** @brief Check if current slot is DL or guard/mixed without UL (i.e., current
* slot is not UL). */
static bool is_tdd_dl_guard_slot(const struct xran_frame_config *frame_conf, int slot)
{
return !is_tdd_ul_symbol(frame_conf, slot, 0);
}
/** @brief Check if current slot is UL or guard/mixed without UL (i.e., current
* slot is not UL). */
static bool is_tdd_ul_guard_slot(const struct xran_frame_config *frame_conf, int slot)
{
return is_tdd_ul_symbol(frame_conf, slot, XRAN_NUM_OF_SYMBOL_PER_SLOT - 1);
}
/** @details xran-specific callback, called when all packets for given CC and
* 1/4, 1/2, 3/4, all symbols of a slot arrived. Read PUSCH data from xran buffers.
* If I/Q compression (bitwidth < 16 bits) is configured, decompresses the data
* before writing. */
void oai_xran_fh_rx_callback(void *pCallbackTag, xran_status_t status, uint8_t mu)
{
if (!first_call_set)
return;
struct xran_cb_tag *callback_tag = (struct xran_cb_tag *)pCallbackTag;
uint32_t port_id = callback_tag->oXuId;
struct xran_fh_init *fh_init = get_xran_fh_init();
struct xran_fh_config *fh_cfg = get_xran_fh_config(port_id);
int num_rx_ant = fh_cfg->neAxc;
int fftsize = 1 << fh_cfg->perMu[mu].nULFftSize;
const int slots_in_sf = 1 << mu;
const int sf_in_frame = 10;
uint32_t tti = callback_tag->slotiId;
uint32_t frame = XranGetFrameNum(tti, 0, sf_in_frame, slots_in_sf);
uint32_t slot = XranGetSlotNum(tti, slots_in_sf * sf_in_frame); // slot within a frame, not a subframe
uint32_t rx_sym = callback_tag->symbol & 0xFF; // rx_sym = 0, 3, 7, 12
LOG_D(HW, "[%d.%d] %s, tti %d rx_sym %d ru_id %d\n", frame, slot, __FUNCTION__, tti, rx_sym, port_id);
RU_t *ru = RC.ru[0];
PHY_VARS_gNB *gNB = ru->gNB_list[0];
if (rx_sym == 7) {
NR_gNB_PUCCH_job_t pucch[MAX_NUM_NR_UCI_PDUS];
NR_gNB_PUSCH_job_t pusch[MAX_UL_PDUS_PER_SLOT];
NR_gNB_SRS_job_t srs[MAX_NUM_NR_SRS_PDUS];
fsn_t now = {.f = frame, .s = slot, .mu = mu};
if (!(get_current_pucch(pucch, &now) || get_current_pusch(pusch, &now) || (get_current_srs(srs, &now) && !fh_cfg->srsEnable))) {
// reset the number of packets
gNB->puxch_received = false;
return;
}
} else { // if not called for full slot, nothing to process and return
return;
}
void *ptr = NULL;
int32_t *pos = NULL;
int idx = 0;
static int outcnt = 0;
int slot_offset_rxdata = 3 & (slot);
uint32_t slot_size = 4 * 14 * fftsize;
uint8_t *start_ptr = NULL;
oran_buf_list_t *bufs = get_xran_buffers(port_id);
for (uint16_t cc_id = 0; cc_id < 1 /*nSectorNum*/; cc_id++) { // OAI does not support multiple CC yet.
for (uint8_t ant_id = 0; ant_id < num_rx_ant; ant_id++) {
uint8_t *rx_data = (uint8_t *)gNB->common_vars.rxdataF[ant_id + (num_rx_ant * port_id)];
start_ptr = rx_data + (slot_size * slot_offset_rxdata);
const struct xran_frame_config *frame_conf = &fh_cfg->frame_conf;
// skip processing this slot is TX (no RX in this slot)
if (!is_tdd_ul_guard_slot(frame_conf, slot))
continue;
// This loop would better be more inner to avoid confusion and maybe also errors.
for (int32_t sym_idx = 0; sym_idx < XRAN_NUM_OF_SYMBOL_PER_SLOT; sym_idx++) {
/* the callback is for mixed and UL slots. In mixed, we have to
* skip DL and guard symbols. */
if (!is_tdd_ul_symbol(frame_conf, slot, sym_idx))
continue;
uint8_t *pPrbMapData = bufs->dstcp[ant_id][tti % XRAN_N_FE_BUF_LEN].pBuffers->pData;
struct xran_prb_map *pRbMap = (struct xran_prb_map *)pPrbMapData;
uint8_t *src = (uint8_t *)ptr;
// even when the fragmentation occurs, nRBSize & nRBStart carry the same values in each prbMap
// therefore, I took the liberty to just extract these values from the first prbMap
int num_totalRB = pRbMap->prbMap[0].nRBSize;
int start_totalRB = pRbMap->prbMap[0].nRBStart;
int32_t local_dst[num_totalRB * N_SC_PER_PRB] __attribute__((aligned(64)));
struct xran_prb_elm *pRbElm = &pRbMap->prbMap[0];
struct xran_rx_packet_ctl *p_rx_packet_ctl = &pRbMap->sFrontHaulRxPacketCtrl[sym_idx];
uint32_t one_rb_size =
(((pRbElm->iqWidth == 0) || (pRbElm->iqWidth == 16)) ? (N_SC_PER_PRB * 2 * 2) : (3 * pRbElm->iqWidth + 1));
int32_t nRxPkt = p_rx_packet_ctl->nRxPkt;
LOG_D(HW, "nRxPkt %d\n", nRxPkt);
for (int pkt_idx = 0; pkt_idx < nRxPkt; pkt_idx++) {
uint8_t *pData;
if (fh_init->mtu < p_rx_packet_ctl->nRBSize[pkt_idx] * one_rb_size)
pData = bufs->dst[ant_id][tti % XRAN_N_FE_BUF_LEN]
.pBuffers[sym_idx % XRAN_NUM_OF_SYMBOL_PER_SLOT]
.pData;
else
pData = p_rx_packet_ctl->pData[pkt_idx];
int numRB = p_rx_packet_ctl->nRBSize[pkt_idx];
int startRB = p_rx_packet_ctl->nRBStart[pkt_idx];
// num_prbu & start_prbu are for UL U-plane only
LOG_D(HW, "p_rx_packet_ctl[%d] startRB[%d]:numRB[%d]\n", pkt_idx, startRB, numRB);
{
{
ptr = pData;
pos = (int32_t *)(start_ptr + (4 * sym_idx * fftsize));
if (ptr == NULL || pos == NULL)
continue;
src = pData;
if (pRbElm->compMethod == XRAN_COMPMETHOD_NONE) {
// NOTE: gcc 11 knows how to generate AVX2 for this!
for (idx = 0; idx < (numRB * N_SC_PER_PRB) * 2; idx++)
((int16_t *)local_dst)[idx + startRB * N_SC_PER_PRB * 2] = ((int16_t)ntohs(((uint16_t *)src)[idx])) >> 2;
} else if (pRbElm->compMethod == XRAN_COMPMETHOD_BLKFLOAT) {
#if defined(__i386__) || defined(__x86_64__)
struct xranlib_decompress_request bfp_decom_req = {};
struct xranlib_decompress_response bfp_decom_rsp = {};
int16_t payload_len = (3 * pRbElm->iqWidth + 1) * numRB;
bfp_decom_req.data_in = (int8_t *)src;
bfp_decom_req.numRBs = numRB;
bfp_decom_req.len = payload_len;
bfp_decom_req.compMethod = pRbElm->compMethod;
bfp_decom_req.iqWidth = pRbElm->iqWidth;
bfp_decom_rsp.data_out = (int16_t *) (local_dst + startRB * N_SC_PER_PRB);
bfp_decom_rsp.len = 0;
xranlib_decompress_avx512(&bfp_decom_req, &bfp_decom_rsp);
#elif defined(__arm__) || defined(__aarch64__)
armral_bfp_decompression(pRbElm->iqWidth, numRB, (int8_t *)src, (int16_t *)local_dst);
#else
AssertFatal(1 == 0, "BFP compression not supported on this architecture");
#endif
outcnt++;
} else {
printf("pRbElm->compMethod == %d is not supported\n", pRbElm->compMethod);
exit(-1);
}
if ((startRB + numRB) == (start_totalRB + num_totalRB)) {
int pos_len = 0;
int neg_len = 0;
if (start_totalRB < (num_totalRB >> 1)) // there are PRBs left of DC
neg_len = min((num_totalRB * 6) - (start_totalRB * 12), num_totalRB * N_SC_PER_PRB);
pos_len = (num_totalRB * N_SC_PER_PRB) - neg_len;
// Calculation of the pointer for the section in the buffer.
// positive half
uint8_t *dst1 = (uint8_t *)(pos + (neg_len == 0 ? ((start_totalRB * N_SC_PER_PRB) - (num_totalRB * 6)) : 0));
// negative half
uint8_t *dst2 = (uint8_t *)(pos + (start_totalRB * N_SC_PER_PRB) + fftsize - (num_totalRB * 6));
memcpy((void *)dst2, (void *)local_dst, neg_len * 4);
memcpy((void *)dst1, (void *)&local_dst[neg_len], pos_len * 4);
}
}
} // idxDesc
} // idxElm
} // sym_ind
} // ant_ind
} // vv_inf
// below only temporary code; shall be moved somewhere
RU_proc_t *proc = &ru->proc;
int slots_per_frame = 10 << (ru->openair0_cfg.nr_scs_for_raster);
proc->tti_rx = slot;
proc->frame_rx = frame;
proc->tti_tx = (slot + ru->sl_ahead) % slots_per_frame;
proc->frame_tx = (slot > (slots_per_frame - 1 - ru->sl_ahead)) ? (frame + 1) & 1023 : frame;
if (proc->first_rx != 0) {
proc->first_rx = 0;
}
gNB->puxch_received = true;
}
// Send CP UL packets
int xran_send_cp_ul_slot(ru_info_t *ru, int frame, int slot)
{
int tti = 20 * frame + slot;
const struct xran_fh_init *fh_init = get_xran_fh_init();
int nb_rx_per_ru = ru->nb_rx / fh_init->xran_ports;
for (uint16_t cc_id = 0; cc_id < 1 /*nSectorNum*/; cc_id++) { // OAI does not support multiple CC yet.
for (uint8_t ant_id = 0; ant_id < ru->nb_rx; ant_id++) {
uint32_t port_id = ant_id / nb_rx_per_ru;
const struct xran_fh_config *fh_cfg = get_xran_fh_config(port_id);
const struct xran_frame_config *frame_conf = &fh_cfg->frame_conf;
// skip processing this slot is TX (no RX in this slot)
if (!is_tdd_ul_guard_slot(frame_conf, slot)) {
continue;
}
// This loop would better be more inner to avoid confusion and maybe also errors.
for (int32_t sym_idx = 0; sym_idx < XRAN_NUM_OF_SYMBOL_PER_SLOT; sym_idx++) {
/* skip DL and guard symbols. */
if (!is_tdd_ul_symbol(frame_conf, slot, sym_idx)) {
continue;
}
oran_buf_list_t *bufs = get_xran_buffers(ant_id / nb_rx_per_ru);
uint8_t *pPrbMapData = bufs->dstcp[ant_id % nb_rx_per_ru][tti % XRAN_N_FE_BUF_LEN].pBuffers->pData;
struct xran_prb_map *pPrbMap = (struct xran_prb_map *)pPrbMapData;
LOG_D(HW, "pPrbMap->nPrbElm %d\n", pPrbMap->nPrbElm);
for (uint32_t idxElm = 0; idxElm < pPrbMap->nPrbElm; idxElm++) {
struct xran_section_desc *p_sec_desc = NULL;
struct xran_prb_elm *pRbElm = &pPrbMap->prbMap[idxElm];
int numRB, startRB;
numRB = pRbElm->UP_nRBSize;
startRB = pRbElm->UP_nRBStart;
p_sec_desc = &pRbElm->sec_desc[sym_idx];
LOG_D(HW, "pPrbMap[%d] : PRBstart %d nPRBs %d\n", idxElm, startRB, numRB);
// For Liteon FR2 with RunSlotPrbMapBySymbolEnable xran_prb_map will have xran_prb_elm prbMap[14], each idxElm matches to sym_idx.
if (fh_cfg->RunSlotPrbMapBySymbolEnable && (sym_idx < pRbElm->nStartSymb || sym_idx >= pRbElm->nStartSymb + pRbElm->numSymb) && !p_sec_desc->pCtrl)
continue;
pRbElm->nBeamIndex = ru->beam_id[slot * XRAN_NUM_OF_SYMBOL_PER_SLOT + sym_idx][ant_id];
}
}
}
}
return (0);
}
/** @details Write PDSCH IQ-data from OAI txdataF_BF buffer to xran buffers. If
* I/Q compression (bitwidth < 16 bits) is configured, compresses the data
* before writing. */
int xran_fh_tx_send_slot(ru_info_t *ru, int frame, int slot, uint64_t timestamp)
{
int tti = /*frame*SUBFRAMES_PER_SYSTEMFRAME*SLOTNUM_PER_SUBFRAME+*/ 20 * frame
+ slot; // commented out temporarily to check that compilation of oran 5g is working.
void *ptr = NULL;
int32_t *pos = NULL;
int idx = 0;
const struct xran_fh_init *fh_init = get_xran_fh_init();
const struct xran_fh_config *fh_cfg = get_xran_fh_config(0);
uint8_t mu_number = fh_cfg->mu_number[0];
int fftsize = 1 << fh_cfg->perMu[mu_number].nDLFftSize;
int nb_tx_per_ru = ru->nb_tx / fh_init->xran_ports;
for (uint16_t cc_id = 0; cc_id < 1 /*nSectorNum*/; cc_id++) { // OAI does not support multiple CC yet.
for (uint8_t ant_id = 0; ant_id < ru->nb_tx; ant_id++) {
oran_buf_list_t *bufs = get_xran_buffers(ant_id / nb_tx_per_ru);
const struct xran_frame_config *frame_conf = &get_xran_fh_config(ant_id / nb_tx_per_ru)->frame_conf;
// skip processing this slot is TX (no TX in this slot)
if (!is_tdd_dl_guard_slot(frame_conf, slot)) {
continue;
}
/* TODO: Remove this hack to set nPrbElm for mixed slot. This can be set statically during init based on TDD pattern. */
if (fh_cfg->RunSlotPrbMapBySymbolEnable) {
uint8_t *pPrbMapData = bufs->srccp[ant_id % nb_tx_per_ru][tti % XRAN_N_FE_BUF_LEN].pBuffers->pData;
struct xran_prb_map *pPrbMap = (struct xran_prb_map *)pPrbMapData;
struct xran_prb_map *pRbMap = pPrbMap;
int32_t dl_sym_end = 0;
for (int32_t sym_idx = 0; sym_idx < XRAN_NUM_OF_SYMBOL_PER_SLOT; sym_idx++) {
if (!is_tdd_dl_symbol(frame_conf, slot, sym_idx)) {
dl_sym_end = sym_idx;
break;
}
}
if (is_tdd_guard_slot(frame_conf, slot))
pRbMap->nPrbElm = dl_sym_end;
else
pRbMap->nPrbElm = XRAN_NUM_OF_SYMBOL_PER_SLOT;
}
// This loop would better be more inner to avoid confusion and maybe also errors.
for (int32_t sym_idx = 0; sym_idx < XRAN_NUM_OF_SYMBOL_PER_SLOT; sym_idx++) {
/* skip UL and guard symbols. */
if (!is_tdd_dl_symbol(frame_conf, slot, sym_idx)) {
continue;
}
uint8_t *pData =
bufs->src[ant_id % nb_tx_per_ru][tti % XRAN_N_FE_BUF_LEN].pBuffers[sym_idx % XRAN_NUM_OF_SYMBOL_PER_SLOT].pData;
uint8_t *pPrbMapData = bufs->srccp[ant_id % nb_tx_per_ru][tti % XRAN_N_FE_BUF_LEN].pBuffers->pData;
struct xran_prb_map *pPrbMap = (struct xran_prb_map *)pPrbMapData;
ptr = pData;
pos = &ru->txdataF_BF[ant_id][sym_idx * fftsize];
uint8_t *u8dptr;
// even when the fragmentation occurs, nRBSize & nRBStart carry the same values in each prbMap
// therefore, I took the liberty to just extract these values from the first prbMap
struct xran_prb_elm *p_prbMapElm = &pPrbMap->prbMap[0];
int num_totalRB = p_prbMapElm->nRBSize;
int start_totalRB = p_prbMapElm->nRBStart;
if (ptr && pos) {
u8dptr = (uint8_t *)ptr;
int16_t payload_len = 0;
uint8_t *dst = (uint8_t *)u8dptr;
for (uint32_t idxElm = 0; idxElm < pPrbMap->nPrbElm; idxElm++) {
struct xran_section_desc *p_sec_desc = NULL;
struct xran_prb_elm *p_prbMapElm = &pPrbMap->prbMap[idxElm];
// radio-transport fragmentation is not supported in xran F release;
// E-bit = 1 => each ethernet frame is considered as the last fragment;
// a group of PRBs per each symbol is encapsulated in one ethernet frame.
// => seems that the RUs don't check for E-bit
p_sec_desc = &p_prbMapElm->sec_desc[sym_idx];
int16_t startRB = p_prbMapElm->UP_nRBStart;
int16_t numRB = p_prbMapElm->UP_nRBSize;
if (p_sec_desc == NULL) {
printf("p_sec_desc == NULL\n");
exit(-1);
}
// For Liteon FR2 with RunSlotPrbMapBySymbolEnable xran_prb_map will have xran_prb_elm prbMap[14], each idxElm matches to sym_idx.
if (fh_cfg->RunSlotPrbMapBySymbolEnable) {
/* skip, if not scheduled */
if(sym_idx < p_prbMapElm->nStartSymb || sym_idx >= p_prbMapElm->nStartSymb + p_prbMapElm->numSymb){
p_sec_desc->iq_buffer_offset = 0;
p_sec_desc->iq_buffer_len = 0;
continue;
}
}
p_prbMapElm->nBeamIndex = ru->beam_id[slot * XRAN_NUM_OF_SYMBOL_PER_SLOT + sym_idx][ant_id];
dst = xran_add_hdr_offset(dst, p_prbMapElm->compMethod);
uint16_t *dst16 = (uint16_t *)dst;
// Start of this section
int32_t *pos_start = pos + (start_totalRB + startRB) * N_SC_PER_PRB;
if (p_prbMapElm->compMethod == XRAN_COMPMETHOD_NONE) {
payload_len = numRB * N_SC_PER_PRB * 4L;
/* convert to Network order */
// NOTE: ggc 11 knows how to generate AVX2 for this!
for (idx = 0; idx < (numRB * N_SC_PER_PRB) * 2; idx++)
((uint16_t *)dst16)[idx] = htons(((uint16_t *)pos_start)[idx]);
} else if (p_prbMapElm->compMethod == XRAN_COMPMETHOD_BLKFLOAT) {
payload_len = (3 * p_prbMapElm->iqWidth + 1) * numRB;
/* Although arm intrinsics natively handle unaligned memory
access, we use a 64 byte aligned input here for maximum
performance. So the src_compr buffer is used for both x86 and arm.
*/
uint32_t src_compr[num_totalRB * N_SC_PER_PRB] __attribute__((aligned(64)));
/* Copy from txdataF with current symbol's PRB start (nRBStart) +
current section's PRB start (UP_nPRBStart) */
memcpy(src_compr, pos_start, (numRB * N_SC_PER_PRB) * sizeof(*pos_start));
#if defined(__i386__) || defined(__x86_64__)
struct xranlib_compress_request bfp_com_req = {};
struct xranlib_compress_response bfp_com_rsp = {};
bfp_com_req.data_in = (int16_t *)src_compr;
bfp_com_req.numRBs = numRB;
bfp_com_req.len = payload_len;
bfp_com_req.compMethod = p_prbMapElm->compMethod;
bfp_com_req.iqWidth = p_prbMapElm->iqWidth;
bfp_com_rsp.data_out = (int8_t *)dst;
bfp_com_rsp.len = 0;
xranlib_compress_avx512(&bfp_com_req, &bfp_com_rsp);
#elif defined(__arm__) || defined(__aarch64__)
armral_bfp_compression(p_prbMapElm->iqWidth, numRB, (int16_t *)src_compr, (int8_t *)dst);
#else
AssertFatal(1 == 0, "BFP compression not supported on this architecture");
#endif
} else {
printf("p_prbMapElm->compMethod == %d is not supported\n", p_prbMapElm->compMethod);
exit(-1);
}
p_sec_desc->iq_buffer_offset = RTE_PTR_DIFF(dst, u8dptr);
p_sec_desc->iq_buffer_len = payload_len;
dst += payload_len;
dst = xran_add_hdr_offset(dst, p_prbMapElm->compMethod);
}
// The tti should be updated as it increased.
pPrbMap->tti_id = tti;
} else {
printf("ptr ==NULL\n");
exit(-1); // fails here??
}
}
}
}
return (0);
}