mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-16 22:20:31 +00:00
Compare commits
2 Commits
develop
...
mac-dl-pip
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
278e004ef3 | ||
|
|
efcc6c521c |
@@ -29,6 +29,8 @@
|
||||
#include "PHY/defs_RU.h"
|
||||
#include "PHY/defs_gNB.h"
|
||||
#include "PHY/defs_nr_common.h"
|
||||
#include "common/utils/nr/nr_common.h"
|
||||
#include "LAYER2/NR_MAC_gNB/mac_proto.h"
|
||||
#include "PHY/impl_defs_nr.h"
|
||||
#include "SCHED_NR/phy_frame_config_nr.h"
|
||||
#include "SCHED_NR/sched_nr.h"
|
||||
@@ -47,16 +49,42 @@
|
||||
#define L1STATSSTRLEN 16384
|
||||
static void rx_func(processingData_L1_t *param);
|
||||
|
||||
static void tx_func(processingData_L1tx_t *info)
|
||||
/// PHY TX processing — runs on L1_tx_thread, pipelined with MAC (L2_tx_thread)
|
||||
static void phy_tx_func(processingData_phyTx_t *info)
|
||||
{
|
||||
PHY_VARS_gNB *gNB = info->gNB;
|
||||
int frame_tx = info->frame_tx;
|
||||
int slot_tx = info->slot_tx;
|
||||
NR_Sched_Rsp_t *sched_response = &info->sched_response;
|
||||
nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config;
|
||||
|
||||
int tx_slot_type = nr_slot_select(cfg, frame_tx, slot_tx);
|
||||
if (tx_slot_type == NR_DOWNLINK_SLOT || tx_slot_type == NR_MIXED_SLOT || get_softmodem_params()->continuous_tx
|
||||
|| IS_SOFTMODEM_RFSIM || cfg->analog_beamforming_ve.analog_bf_vendor_ext.value) {
|
||||
start_meas(&gNB->phy_proc_tx);
|
||||
phy_procedures_gNB_TX(gNB, &sched_response->DL_req, &sched_response->TX_req, &sched_response->UL_dci_req, frame_tx, slot_tx);
|
||||
|
||||
processingData_RU_t syncMsgRU;
|
||||
syncMsgRU.frame_tx = frame_tx;
|
||||
syncMsgRU.slot_tx = slot_tx;
|
||||
syncMsgRU.ru = gNB->RU_list[0];
|
||||
syncMsgRU.timestamp_tx = info->timestamp_tx;
|
||||
LOG_D(PHY, "gNB: %d.%d : calling RU TX function\n", syncMsgRU.frame_tx, syncMsgRU.slot_tx);
|
||||
ru_tx_func((void *)&syncMsgRU);
|
||||
stop_meas(&gNB->phy_proc_tx);
|
||||
}
|
||||
}
|
||||
|
||||
/// MAC scheduling — runs on L2_tx_thread, dispatches PHY TX work to L1_tx_thread
|
||||
static void mac_sched_func(processingData_L1tx_t *info)
|
||||
{
|
||||
int frame_tx = info->frame;
|
||||
int slot_tx = info->slot;
|
||||
int frame_rx = info->frame_rx;
|
||||
int slot_rx = info->slot_rx;
|
||||
LOG_D(NR_PHY, "%d.%d running tx_func\n", frame_tx, slot_tx);
|
||||
LOG_D(NR_PHY, "%d.%d running mac_sched_func\n", frame_tx, slot_tx);
|
||||
PHY_VARS_gNB *gNB = info->gNB;
|
||||
NR_IF_Module_t *ifi = gNB->if_inst;
|
||||
nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config;
|
||||
|
||||
T(T_GNB_PHY_DL_TICK, T_INT(gNB->Mod_id), T_INT(frame_tx), T_INT(slot_tx));
|
||||
|
||||
@@ -65,22 +93,38 @@ static void tx_func(processingData_L1tx_t *info)
|
||||
reset_active_ulsch(gNB, frame_rx);
|
||||
}
|
||||
|
||||
clear_slot_beamid(gNB, slot_tx);
|
||||
// Pull a pre-allocated sched_response buffer from the free-list pool.
|
||||
// Blocks if all buffers are in-flight (backpressure: late but correct, same as serial).
|
||||
notifiedFIFO_elt_t *sched_elt = pullNotifiedFIFO(&gNB->sched_free_list);
|
||||
if (sched_elt == NULL)
|
||||
return; // shutdown
|
||||
processingData_phyTx_t *phyTxMsg = NotifiedFifoData(sched_elt);
|
||||
NR_Sched_Rsp_t *cur_resp = &phyTxMsg->sched_response;
|
||||
|
||||
// Drain pending UL indications from L1_rx before running the scheduler,
|
||||
// so that RACH/UCI/ULSCH/SRS state is up-to-date when scheduling decisions are made.
|
||||
{
|
||||
unsigned int r = atomic_load_explicit(&gNB->ul_ind_read, memory_order_relaxed);
|
||||
unsigned int w = atomic_load_explicit(&gNB->ul_ind_write, memory_order_acquire);
|
||||
start_meas(&gNB->ul_indication_stats);
|
||||
while (r < w) {
|
||||
NR_UL_IND_t *ul_info = &gNB->ul_ind_pool[r % UL_IND_POOL_SIZE];
|
||||
ifi->NR_UL_indication(ul_info);
|
||||
r++;
|
||||
}
|
||||
stop_meas(&gNB->ul_indication_stats);
|
||||
atomic_store_explicit(&gNB->ul_ind_read, r, memory_order_release);
|
||||
}
|
||||
|
||||
nfapi_nr_slot_indication_scf_t ind = {.sfn = frame_tx, .slot = slot_tx};
|
||||
start_meas(&gNB->slot_indication_stats);
|
||||
// this variable is very big (multiple MB), so we put it into static storage
|
||||
// to not overflow the stack while still having it in local (function) scope
|
||||
// also, tx_func() is only executed by one thread, serially
|
||||
static NR_Sched_Rsp_t sched_response;
|
||||
ifi->NR_slot_indication(&ind, &sched_response);
|
||||
ifi->NR_slot_indication(&ind, cur_resp);
|
||||
stop_meas(&gNB->slot_indication_stats);
|
||||
|
||||
info->gNB = gNB;
|
||||
// Copy UL PDUs to SPSC queues (safe — copies data, no lasting reference to sched_response)
|
||||
nr_save_ul_tti_req(gNB, &cur_resp->UL_tti_req);
|
||||
|
||||
// At this point, MAC scheduler just ran, including scheduling
|
||||
// PRACH/PUCCH/PUSCH, so trigger RX chain processing
|
||||
nr_save_ul_tti_req(gNB, &sched_response.UL_tti_req);
|
||||
// Trigger RX chain processing
|
||||
LOG_D(NR_PHY, "Trigger RX for %d.%d\n", frame_rx, slot_rx);
|
||||
notifiedFIFO_elt_t *res = newNotifiedFIFO_elt(sizeof(processingData_L1_t), 0, &gNB->resp_L1, NULL);
|
||||
processingData_L1_t *syncMsg = NotifiedFifoData(res);
|
||||
@@ -91,23 +135,15 @@ static void tx_func(processingData_L1tx_t *info)
|
||||
res->key = slot_rx;
|
||||
pushNotifiedFIFO(&gNB->resp_L1, res);
|
||||
|
||||
int tx_slot_type = nr_slot_select(cfg, frame_tx, slot_tx);
|
||||
// TODO check for analog_bf_vendor_ext set to 1 is a workaround while no beam API for beam selection is implemented
|
||||
if (tx_slot_type == NR_DOWNLINK_SLOT || tx_slot_type == NR_MIXED_SLOT || get_softmodem_params()->continuous_tx
|
||||
|| IS_SOFTMODEM_RFSIM || cfg->analog_beamforming_ve.analog_bf_vendor_ext.value) {
|
||||
start_meas(&info->gNB->phy_proc_tx);
|
||||
phy_procedures_gNB_TX(info->gNB, &sched_response.DL_req, &sched_response.TX_req, &sched_response.UL_dci_req, frame_tx,slot_tx);
|
||||
|
||||
PHY_VARS_gNB *gNB = info->gNB;
|
||||
processingData_RU_t syncMsgRU;
|
||||
syncMsgRU.frame_tx = frame_tx;
|
||||
syncMsgRU.slot_tx = slot_tx;
|
||||
syncMsgRU.ru = gNB->RU_list[0];
|
||||
syncMsgRU.timestamp_tx = info->timestamp_tx;
|
||||
LOG_D(PHY, "gNB: %d.%d : calling RU TX function\n", syncMsgRU.frame_tx, syncMsgRU.slot_tx);
|
||||
ru_tx_func((void *)&syncMsgRU);
|
||||
stop_meas(&info->gNB->phy_proc_tx);
|
||||
}
|
||||
// Dispatch PHY TX to L1_tx_thread — runs concurrently with next slot's MAC scheduling.
|
||||
// The pool element carries the embedded sched_response and cycles back to sched_free_list
|
||||
// after L1 processing.
|
||||
clear_slot_beamid(gNB, slot_tx);
|
||||
phyTxMsg->gNB = gNB;
|
||||
phyTxMsg->frame_tx = frame_tx;
|
||||
phyTxMsg->slot_tx = slot_tx;
|
||||
phyTxMsg->timestamp_tx = info->timestamp_tx;
|
||||
pushNotifiedFIFO(&gNB->L1_tx_out, sched_elt);
|
||||
}
|
||||
|
||||
void *L1_rx_thread(void *arg)
|
||||
@@ -126,18 +162,72 @@ void *L1_rx_thread(void *arg)
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
// Added for URLLC, requires MAC scheduling to be split from UL indication
|
||||
/// L1 TX thread: runs PHY TX processing (encoding, modulation, RU TX)
|
||||
void *L1_tx_thread(void *arg) {
|
||||
PHY_VARS_gNB *gNB = (PHY_VARS_gNB*)arg;
|
||||
|
||||
while (oai_exit == 0) {
|
||||
notifiedFIFO_elt_t *res = pullNotifiedFIFO(&gNB->L1_tx_out);
|
||||
if (res == NULL) // stopping condition, happens only when queue is freed
|
||||
if (res == NULL) // stopping condition, happens only when queue is aborted
|
||||
break;
|
||||
processingData_phyTx_t *info = (processingData_phyTx_t *)NotifiedFifoData(res);
|
||||
start_meas(&gNB->l1_tx_proc);
|
||||
phy_tx_func(info);
|
||||
stop_meas(&gNB->l1_tx_proc);
|
||||
// Recycle pool element back to free-list for MAC to reuse
|
||||
pushNotifiedFIFO(&gNB->sched_free_list, res);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// L2 UL thread: pre-computes UL scheduling for the upcoming TDD period.
|
||||
/// Runs without sched_lock — safe because during TDD DL-only slots no L1 RX
|
||||
/// indications modify UE state, and per-slot resources (CCE, VRB, beam) are
|
||||
/// indexed by slot so no conflict with the DL thread processing a different slot.
|
||||
void *L2_UL_TDD_thread(void *arg) {
|
||||
PHY_VARS_gNB *gNB = (PHY_VARS_gNB *)arg;
|
||||
gNB_MAC_INST *mac = RC.nrmac[0];
|
||||
const frame_structure_t *fs = &mac->frame_structure;
|
||||
const int period_len = fs->numb_slots_period;
|
||||
const int slots_frame = fs->numb_slots_frame;
|
||||
|
||||
// Bypass NR_SCHED_ENSURE_LOCKED asserts — this thread runs without sched_lock
|
||||
nr_sched_lock_bypassed = true;
|
||||
|
||||
while (oai_exit == 0) {
|
||||
// Wait for trigger from DL thread (first DL slot of TDD period)
|
||||
pthread_mutex_lock(&mac->ul_precomp_mutex);
|
||||
while (!mac->ul_precomp_start_req && oai_exit == 0)
|
||||
pthread_cond_wait(&mac->ul_precomp_start_cond, &mac->ul_precomp_mutex);
|
||||
if (oai_exit) { pthread_mutex_unlock(&mac->ul_precomp_mutex); break; }
|
||||
mac->ul_precomp_start_req = false;
|
||||
const int frame = mac->ul_precomp_frame;
|
||||
const int slot0 = mac->ul_precomp_slot;
|
||||
pthread_mutex_unlock(&mac->ul_precomp_mutex);
|
||||
|
||||
// Pre-compute UL scheduling for all DL slots in this TDD period
|
||||
for (int i = 0; i < period_len; i++) {
|
||||
mac->ul_precomp_dci[i].numPdus = 0;
|
||||
const int f = (frame + (slot0 + i) / slots_frame) % 1024;
|
||||
const int s = (slot0 + i) % slots_frame;
|
||||
if (is_dl_slot(s, fs))
|
||||
nr_schedule_ulsch(0, f, s, &mac->ul_precomp_dci[i]);
|
||||
atomic_store_explicit(&mac->ul_precomp_slots_done, i + 1, memory_order_release);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// L2 TX thread: runs MAC scheduling, dispatches PHY TX to L1_tx_thread
|
||||
void *L2_tx_thread(void *arg) {
|
||||
PHY_VARS_gNB *gNB = (PHY_VARS_gNB*)arg;
|
||||
|
||||
while (oai_exit == 0) {
|
||||
notifiedFIFO_elt_t *res = pullNotifiedFIFO(&gNB->L2_tx_out);
|
||||
if (res == NULL)
|
||||
break;
|
||||
processingData_L1tx_t *info = (processingData_L1tx_t *)NotifiedFifoData(res);
|
||||
start_meas(&gNB->l1_tx_proc);
|
||||
tx_func(info);
|
||||
stop_meas(&gNB->l1_tx_proc);
|
||||
mac_sched_func(info);
|
||||
delNotifiedFIFO_elt(res);
|
||||
}
|
||||
return NULL;
|
||||
@@ -157,16 +247,20 @@ static void rx_func(processingData_L1_t *info)
|
||||
if (rx_slot_type == NR_UPLINK_SLOT || rx_slot_type == NR_MIXED_SLOT) {
|
||||
LOG_D(NR_PHY, "%d.%d Starting RX processing\n", frame_rx, slot_rx);
|
||||
|
||||
// UE-specific RX processing for subframe n
|
||||
NR_UL_IND_t UL_INFO = {.frame = frame_rx, .slot = slot_rx, .module_id = gNB->Mod_id, .CC_id = gNB->CC_id};
|
||||
// Acquire a pool entry for UL indications (ring buffer: L1_rx writes, scheduler drains)
|
||||
unsigned int w = atomic_load_explicit(&gNB->ul_ind_write, memory_order_relaxed);
|
||||
unsigned int r = atomic_load_explicit(&gNB->ul_ind_read, memory_order_acquire);
|
||||
AssertFatal(w - r < UL_IND_POOL_SIZE, "%d.%d UL indication pool full (%u written, %u read)\n", frame_rx, slot_rx, w, r);
|
||||
NR_UL_IND_t *UL_INFO = &gNB->ul_ind_pool[w % UL_IND_POOL_SIZE];
|
||||
*UL_INFO = (NR_UL_IND_t){.frame = frame_rx, .slot = slot_rx, .module_id = gNB->Mod_id, .CC_id = gNB->CC_id};
|
||||
// Do PRACH RU processing
|
||||
UL_INFO.rach_ind.pdu_list = UL_INFO.prach_pdu_indication_list;
|
||||
UL_INFO.rach_ind.number_of_pdus = 0;
|
||||
UL_INFO->rach_ind.pdu_list = UL_INFO->prach_pdu_indication_list;
|
||||
UL_INFO->rach_ind.number_of_pdus = 0;
|
||||
// even if processing is late, we might collect all PRACH
|
||||
// the last PRACH's frame/slot is when all UE's appear to have accessed
|
||||
prach_item_t p;
|
||||
while (spsc_q_get(&gNB->prach_l1rx_queue, &p, sizeof(p)))
|
||||
L1_nr_prach_procedures(gNB, &p, &UL_INFO.rach_ind);
|
||||
L1_nr_prach_procedures(gNB, &p, &UL_INFO->rach_ind);
|
||||
|
||||
//WA: comment rotation in tx/rx
|
||||
if (gNB->phase_comp) {
|
||||
@@ -185,12 +279,10 @@ static void rx_func(processingData_L1_t *info)
|
||||
}
|
||||
}
|
||||
}
|
||||
phy_procedures_gNB_uespec_RX(gNB, frame_rx, slot_rx, &UL_INFO);
|
||||
phy_procedures_gNB_uespec_RX(gNB, frame_rx, slot_rx, UL_INFO);
|
||||
|
||||
// Call the scheduler
|
||||
start_meas(&gNB->ul_indication_stats);
|
||||
gNB->if_inst->NR_UL_indication(&UL_INFO);
|
||||
stop_meas(&gNB->ul_indication_stats);
|
||||
// Publish filled entry for the scheduler to drain (no NR_UL_indication here)
|
||||
atomic_store_explicit(&gNB->ul_ind_write, w + 1, memory_order_release);
|
||||
|
||||
notifiedFIFO_elt_t *res = newNotifiedFIFO_elt(sizeof(processingData_L1_t), 0, &gNB->L1_rx_out, NULL);
|
||||
processingData_L1_t *syncMsg = NotifiedFifoData(res);
|
||||
@@ -317,14 +409,54 @@ void init_gNB_Tpool(int inst)
|
||||
|
||||
// L1 RX result FIFO
|
||||
initNotifiedFIFO(&gNB->resp_L1);
|
||||
// L1 TX result FIFO
|
||||
// L1 TX FIFO: L2 (MAC) → L1 (PHY TX)
|
||||
initNotifiedFIFO(&gNB->L1_tx_out);
|
||||
// L2 TX FIFO: RU → L2 (MAC scheduling)
|
||||
initNotifiedFIFO(&gNB->L2_tx_out);
|
||||
initNotifiedFIFO(&gNB->L1_rx_out);
|
||||
|
||||
// create the RX thread responsible for RX processing start event (resp_L1 msg queue), then launch rx_func()
|
||||
// Pre-allocated pool of NR_Sched_Rsp_t buffers for MAC/PHY pipelining.
|
||||
// Pool size: TDD = max(slots_per_TDD_period, 4), FDD = 4.
|
||||
// Backpressure: MAC blocks when pool is exhausted (late but correct).
|
||||
NR_DL_FRAME_PARMS *fp = &gNB->frame_parms;
|
||||
int pool_size;
|
||||
if (fp->frame_type == TDD) {
|
||||
int slots_per_tdd = fp->slots_per_frame / get_nb_periods_per_frame(gNB->gNB_config.tdd_table.tdd_period.value);
|
||||
pool_size = slots_per_tdd > 4 ? slots_per_tdd : 4;
|
||||
} else {
|
||||
pool_size = 4;
|
||||
}
|
||||
gNB->sched_pool_size = pool_size;
|
||||
gNB->sched_pool = calloc(pool_size, sizeof(notifiedFIFO_elt_t *));
|
||||
AssertFatal(gNB->sched_pool, "Failed to allocate sched_pool array\n");
|
||||
initNotifiedFIFO(&gNB->sched_free_list);
|
||||
for (int i = 0; i < pool_size; i++) {
|
||||
notifiedFIFO_elt_t *elt = newNotifiedFIFO_elt(sizeof(processingData_phyTx_t), 0, NULL, NULL);
|
||||
elt->malloced = false; // pool-managed: don't let abortNotifiedFIFO free these
|
||||
gNB->sched_pool[i] = elt;
|
||||
pushNotifiedFIFO(&gNB->sched_free_list, elt);
|
||||
}
|
||||
LOG_I(PHY, "Allocated %d sched_response pool buffers (%s, %zu bytes each)\n",
|
||||
pool_size, fp->frame_type == TDD ? "TDD" : "FDD", sizeof(processingData_phyTx_t));
|
||||
|
||||
// L1 RX thread: PUSCH/PUCCH/SRS/PRACH processing
|
||||
threadCreate(&gNB->L1_rx_thread, L1_rx_thread, (void *)gNB, "L1_rx_thread", gNB->L1_rx_thread_core, OAI_PRIORITY_RT_MAX);
|
||||
// create the TX thread responsible for TX processing start event (L1_tx_out msg queue), then launch tx_func()
|
||||
// L1 TX thread: PHY TX processing (LDPC encoding, modulation, RU TX)
|
||||
threadCreate(&gNB->L1_tx_thread, L1_tx_thread, (void *)gNB, "L1_tx_thread", gNB->L1_tx_thread_core, OAI_PRIORITY_RT_MAX);
|
||||
// L2 TX thread: MAC scheduling, pipelined with L1 TX
|
||||
gNB_MAC_INST *mac = RC.nrmac[0];
|
||||
threadCreate(&mac->L2_tx_thread, L2_tx_thread, (void *)gNB, "L2_tx_thread", mac->L2_tx_thread_core, OAI_PRIORITY_RT_MAX);
|
||||
|
||||
// L2 UL thread: TDD UL pre-scheduling on dedicated core
|
||||
if (fp->frame_type == TDD) {
|
||||
int period_len = fp->slots_per_frame / get_nb_periods_per_frame(gNB->gNB_config.tdd_table.tdd_period.value);
|
||||
mac->ul_precomp_dci = calloc(period_len, sizeof(*mac->ul_precomp_dci));
|
||||
AssertFatal(mac->ul_precomp_dci, "ul_precomp_dci alloc failed\n");
|
||||
mac->ul_precomp_period_len = period_len;
|
||||
pthread_mutex_init(&mac->ul_precomp_mutex, NULL);
|
||||
pthread_cond_init(&mac->ul_precomp_start_cond, NULL);
|
||||
threadCreate(&mac->L2_UL_TDD_thread, L2_UL_TDD_thread, (void *)gNB, "L2_UL_TDD_thread", mac->L2_ul_tdd_thread_core, OAI_PRIORITY_RT_MAX);
|
||||
}
|
||||
|
||||
if (!IS_SOFTMODEM_NOSTATS)
|
||||
threadCreate(&proc->L1_stats_thread, nrL1_stats_thread, (void *)gNB, "L1_stats", -1, OAI_PRIORITY_RT_LOW);
|
||||
@@ -334,9 +466,31 @@ void term_gNB_Tpool(int inst) {
|
||||
PHY_VARS_gNB *gNB = RC.gNB[inst];
|
||||
abortNotifiedFIFO(&gNB->resp_L1);
|
||||
pthread_join(gNB->L1_rx_thread, NULL);
|
||||
// Abort sched_free_list first to unblock MAC if it's waiting on backpressure
|
||||
gNB_MAC_INST *mac = RC.nrmac[0];
|
||||
abortNotifiedFIFO(&gNB->sched_free_list);
|
||||
abortNotifiedFIFO(&gNB->L2_tx_out);
|
||||
pthread_join(mac->L2_tx_thread, NULL);
|
||||
abortNotifiedFIFO(&gNB->L1_tx_out);
|
||||
pthread_join(gNB->L1_tx_thread, NULL);
|
||||
|
||||
// Stop UL pre-scheduling thread (TDD only)
|
||||
if (gNB->frame_parms.frame_type == TDD) {
|
||||
pthread_mutex_lock(&mac->ul_precomp_mutex);
|
||||
mac->ul_precomp_start_req = true; // wake up so it sees oai_exit
|
||||
pthread_cond_signal(&mac->ul_precomp_start_cond);
|
||||
pthread_mutex_unlock(&mac->ul_precomp_mutex);
|
||||
pthread_join(mac->L2_UL_TDD_thread, NULL);
|
||||
free(mac->ul_precomp_dci);
|
||||
pthread_mutex_destroy(&mac->ul_precomp_mutex);
|
||||
pthread_cond_destroy(&mac->ul_precomp_start_cond);
|
||||
}
|
||||
|
||||
// Free pool elements (malloced=false so abort didn't free them)
|
||||
for (int i = 0; i < gNB->sched_pool_size; i++)
|
||||
free(gNB->sched_pool[i]);
|
||||
free(gNB->sched_pool);
|
||||
|
||||
abortTpool(&gNB->threadPool);
|
||||
abortNotifiedFIFO(&gNB->L1_rx_out);
|
||||
|
||||
|
||||
@@ -995,7 +995,7 @@ void *ru_thread(void *param)
|
||||
} // end if (ru->feprx)
|
||||
} // end if (slot_type == NR_UPLINK_SLOT || slot_type == NR_MIXED_SLOT) {
|
||||
|
||||
notifiedFIFO_elt_t *resTx = newNotifiedFIFO_elt(sizeof(processingData_L1tx_t), 0, &gNB->L1_tx_out, NULL);
|
||||
notifiedFIFO_elt_t *resTx = newNotifiedFIFO_elt(sizeof(processingData_L1tx_t), 0, &gNB->L2_tx_out, NULL);
|
||||
resTx->key = proc->tti_tx;
|
||||
processingData_L1tx_t *syncMsgTx = NotifiedFifoData(resTx);
|
||||
*syncMsgTx = (processingData_L1tx_t){.gNB = gNB,
|
||||
@@ -1004,7 +1004,7 @@ void *ru_thread(void *param)
|
||||
.frame_rx = proc->frame_rx,
|
||||
.slot_rx = proc->tti_rx,
|
||||
.timestamp_tx = proc->timestamp_tx};
|
||||
pushNotifiedFIFO(&gNB->L1_tx_out, resTx);
|
||||
pushNotifiedFIFO(&gNB->L2_tx_out, resTx);
|
||||
}
|
||||
|
||||
ru_thread_status = 0;
|
||||
|
||||
@@ -168,6 +168,12 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB)
|
||||
// PRACH
|
||||
init_nr_prach(gNB);
|
||||
|
||||
// L1→L2 UL indication pool (ring buffer drained by scheduler)
|
||||
gNB->ul_ind_pool = calloc(UL_IND_POOL_SIZE, sizeof(NR_UL_IND_t));
|
||||
AssertFatal(gNB->ul_ind_pool, "Failed to allocate UL indication pool\n");
|
||||
atomic_init(&gNB->ul_ind_write, 0);
|
||||
atomic_init(&gNB->ul_ind_read, 0);
|
||||
|
||||
int N_RB_UL = cfg->carrier_config.ul_grid_size[cfg->ssb_config.scs_common.value].value;
|
||||
int n_buf = Prx*max_ul_mimo_layers;
|
||||
|
||||
@@ -209,6 +215,8 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB)
|
||||
|
||||
reset_nr_transport(gNB);
|
||||
reset_nr_prach(gNB);
|
||||
free(gNB->ul_ind_pool);
|
||||
gNB->ul_ind_pool = NULL;
|
||||
|
||||
destroy_DLSCH_struct(gNB);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "common/utils/threadPool/task_ans.h"
|
||||
#include "openair1/PHY/defs_RU.h"
|
||||
#include "common/utils/ds/spsc_q.h"
|
||||
#include <stdatomic.h>
|
||||
|
||||
#define MAX_NUM_RU_PER_gNB 8
|
||||
#define MAX_PUCCH0_NID 8
|
||||
@@ -382,6 +383,12 @@ typedef struct PHY_VARS_gNB_s {
|
||||
spsc_q_t pucch_queue;
|
||||
spsc_q_t pusch_queue;
|
||||
spsc_q_t srs_queue;
|
||||
|
||||
/// L1→L2 UL indication ring buffer: L1_rx writes, scheduler drains
|
||||
#define UL_IND_POOL_SIZE 16
|
||||
NR_UL_IND_t *ul_ind_pool; ///< pool of UL indication buffers
|
||||
_Atomic unsigned int ul_ind_write; ///< L1_rx increments after filling pool entry
|
||||
_Atomic unsigned int ul_ind_read; ///< scheduler increments after processing pool entry
|
||||
NR_gNB_ULSCH_t *ulsch;
|
||||
NR_gNB_PHY_STATS_t phy_stats[MAX_MOBILES_PER_GNB];
|
||||
t_nrPolar_params **polarParams;
|
||||
@@ -478,7 +485,17 @@ typedef struct PHY_VARS_gNB_s {
|
||||
time_stats_t srs_iq_matrix_stats;
|
||||
|
||||
notifiedFIFO_t resp_L1;
|
||||
notifiedFIFO_t L1_tx_out;
|
||||
notifiedFIFO_t L1_tx_out; ///< L2 (MAC) → L1 TX (PHY TX) FIFO
|
||||
notifiedFIFO_t L2_tx_out; ///< RU → L2 (MAC scheduling) FIFO
|
||||
/// Pool of pre-allocated processingData_phyTx_t buffers.
|
||||
/// Single-owner handoff pattern — each element is owned by exactly one thread at a time:
|
||||
/// sched_free_list → MAC (fills it) → L1_tx_out → PHY (reads it) → sched_free_list
|
||||
/// No concurrent access: MAC gives up the element on push, PHY acquires it on pull.
|
||||
/// The FIFO mutex provides the memory barrier ensuring PHY sees all MAC writes.
|
||||
/// Backpressure: MAC blocks on pull if all elements are in-flight (late but correct).
|
||||
notifiedFIFO_t sched_free_list;
|
||||
notifiedFIFO_elt_t **sched_pool; ///< Array of pool element pointers (for cleanup at shutdown)
|
||||
int sched_pool_size;
|
||||
notifiedFIFO_t L1_rx_out;
|
||||
tpool_t threadPool;
|
||||
int num_pusch_symbols_per_thread;
|
||||
@@ -486,7 +503,7 @@ typedef struct PHY_VARS_gNB_s {
|
||||
int dmrs_num_antennas_per_thread;
|
||||
pthread_t L1_rx_thread;
|
||||
int L1_rx_thread_core;
|
||||
pthread_t L1_tx_thread;
|
||||
pthread_t L1_tx_thread; ///< PHY TX processing thread
|
||||
int L1_tx_thread_core;
|
||||
void *scopeData;
|
||||
} PHY_VARS_gNB;
|
||||
@@ -566,6 +583,16 @@ typedef struct processingData_L1tx {
|
||||
PHY_VARS_gNB *gNB;
|
||||
} processingData_L1tx_t;
|
||||
|
||||
/// Data passed to the PHY TX thread for pipelined MAC/PHY processing.
|
||||
/// Each element is pre-allocated in a pool and cycles between sched_free_list and L1_tx_out.
|
||||
typedef struct processingData_phyTx {
|
||||
int frame_tx;
|
||||
int slot_tx;
|
||||
openair0_timestamp_t timestamp_tx;
|
||||
PHY_VARS_gNB *gNB;
|
||||
NR_Sched_Rsp_t sched_response; ///< embedded sched_response (pool-managed, no malloc in steady state)
|
||||
} processingData_phyTx_t;
|
||||
|
||||
typedef struct processingData_L1rx {
|
||||
int frame_rx;
|
||||
int slot_rx;
|
||||
|
||||
@@ -971,6 +971,8 @@ int main(int argc, char **argv)
|
||||
int ret = 1;
|
||||
initNamedTpool(gNBthreads, &gNB->threadPool, true, "gNB-tpool");
|
||||
initNotifiedFIFO(&gNB->L1_tx_out);
|
||||
initNotifiedFIFO(&gNB->L2_tx_out);
|
||||
initNotifiedFIFO(&gNB->sched_free_list);
|
||||
|
||||
// Buffers to store internal memory of slot process
|
||||
int rx_size = (((14 * UE->frame_parms.N_RB_DL * 12 * sizeof(int32_t)) + 15) >> 4) << 4;
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
#define MACRLC_PUSCH_RSSI_THRESHOLD "pusch_RSSI_Threshold"
|
||||
#define MACRLC_PUCCH_RSSI_THRESHOLD "pucch_RSSI_Threshold"
|
||||
#define MACRLC_STATS_MAX_UE "stats_max_ue"
|
||||
#define MACRLC_L2_TX_THREAD_CORE "L2_tx_thread_core"
|
||||
#define MACRLC_L2_UL_TDD_THREAD_CORE "L2_ul_tdd_thread_core"
|
||||
|
||||
#define HLP_MACRLC_UL_PRBBLACK "SNR threshold to decide whether a PRB will be blacklisted or not"
|
||||
#define HLP_MACRLC_DL_BLER_UP "Upper threshold of BLER to decrease DL MCS"
|
||||
@@ -123,6 +125,8 @@
|
||||
{MACRLC_PUCCH_RSSI_THRESHOLD, HLP_MACRLC_PUCCH_RSSI_THRESHOLD, \
|
||||
0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
|
||||
{MACRLC_STATS_MAX_UE, HLP_MACRLC_STATS_MAX_UE, 0, .iptr=NULL, .defintval=8, TYPE_INT, 0}, \
|
||||
{MACRLC_L2_TX_THREAD_CORE, NULL, 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \
|
||||
{MACRLC_L2_UL_TDD_THREAD_CORE, NULL, 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \
|
||||
}
|
||||
// clang-format off
|
||||
|
||||
@@ -169,6 +173,8 @@
|
||||
{ .s2 = { config_check_intrange, {-1280, 0}} }, /* PUSCH RSSI threshold range */ \
|
||||
{ .s2 = { config_check_intrange, {-1280, 0}} }, /* PUCCH RSSI threshold range */ \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
@@ -1711,6 +1711,10 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg)
|
||||
}
|
||||
RC.nrmac[j]->ulsch_max_frame_inactivity = *gpd(params, np, MACRLC_ULSCH_MAX_FRAME_INACTIVITY)->uptr;
|
||||
RC.nrmac[j]->stats_max_ue = *gpd(params, np, MACRLC_STATS_MAX_UE)->iptr;
|
||||
RC.nrmac[j]->L2_tx_thread_core = *gpd(params, np, MACRLC_L2_TX_THREAD_CORE)->iptr;
|
||||
RC.nrmac[j]->L2_ul_tdd_thread_core = *gpd(params, np, MACRLC_L2_UL_TDD_THREAD_CORE)->iptr;
|
||||
LOG_I(NR_MAC, "L2_tx_thread_core %d, L2_ul_tdd_thread_core %d\n",
|
||||
RC.nrmac[j]->L2_tx_thread_core, RC.nrmac[j]->L2_ul_tdd_thread_core);
|
||||
RC.nrmac[j]->print_ue_stats = RC.nrmac[j]->stats_max_ue > 0;
|
||||
NR_bler_options_t *dl_bler_options = &RC.nrmac[j]->dl_bler;
|
||||
dl_bler_options->upper = *gpd(params, np, MACRLC_DL_BLER_TARGET_UPPER)->dblptr;
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
__thread bool nr_sched_lock_bypassed = false;
|
||||
|
||||
uint8_t nr_get_rv(int rel_round)
|
||||
{
|
||||
const uint8_t nr_rv_round_map[4] = {0, 2, 3, 1};
|
||||
@@ -213,7 +215,40 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo
|
||||
|
||||
// This schedules the DCI for Uplink and subsequently PUSCH
|
||||
start_meas(&gNB->schedule_ulsch);
|
||||
nr_schedule_ulsch(module_idP, frame, slot, &sched_info->UL_dci_req);
|
||||
if (scc->tdd_UL_DL_ConfigurationCommon && gNB->ul_precomp_dci) {
|
||||
// TDD with UL pre-scheduling thread active
|
||||
const frame_structure_t *fs = &gNB->frame_structure;
|
||||
const int slot_in_period = slot % fs->numb_slots_period;
|
||||
int done = atomic_load_explicit(&gNB->ul_precomp_slots_done, memory_order_acquire);
|
||||
if (slot_in_period == 0) {
|
||||
// Verify previous period completed, then reset and trigger new period
|
||||
int prev = done;
|
||||
AssertFatal(prev == 0 || prev >= fs->numb_slots_period,
|
||||
"%d.%d previous UL pre-computation incomplete (%d/%d)\n",
|
||||
frame, slot, prev, fs->numb_slots_period);
|
||||
atomic_store_explicit(&gNB->ul_precomp_slots_done, 0, memory_order_release);
|
||||
pthread_mutex_lock(&gNB->ul_precomp_mutex);
|
||||
gNB->ul_precomp_frame = frame;
|
||||
gNB->ul_precomp_slot = slot;
|
||||
gNB->ul_precomp_start_req = true;
|
||||
pthread_cond_signal(&gNB->ul_precomp_start_cond);
|
||||
pthread_mutex_unlock(&gNB->ul_precomp_mutex);
|
||||
done = 0; // just reset
|
||||
}
|
||||
// At startup the first slot may land mid-period (e.g. ORAN timing sync),
|
||||
// before the UL thread has ever been triggered. Schedule inline until the
|
||||
// first period boundary (slot_in_period == 0) is reached.
|
||||
if (done == 0 && slot_in_period != 0) {
|
||||
nr_schedule_ulsch(module_idP, frame, slot, &sched_info->UL_dci_req);
|
||||
} else {
|
||||
while (atomic_load_explicit(&gNB->ul_precomp_slots_done, memory_order_acquire) <= slot_in_period)
|
||||
;
|
||||
if (gNB->ul_precomp_dci[slot_in_period].numPdus > 0)
|
||||
sched_info->UL_dci_req = gNB->ul_precomp_dci[slot_in_period];
|
||||
}
|
||||
} else {
|
||||
nr_schedule_ulsch(module_idP, frame, slot, &sched_info->UL_dci_req);
|
||||
}
|
||||
stop_meas(&gNB->schedule_ulsch);
|
||||
|
||||
// This schedules the DCI for Downlink and PDSCH
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <string.h>
|
||||
#include <complex.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include "fsn.h"
|
||||
#include "common/utils/ds/seq_arr.h"
|
||||
#include "common/utils/nr/nr_common.h"
|
||||
@@ -34,10 +35,16 @@
|
||||
AssertFatal(rc == 0, "error while locking scheduler mutex, pthread_mutex_unlock() returned %d\n", rc); \
|
||||
} while (0)
|
||||
|
||||
/// Set to true on the UL pre-computation thread so it can call scheduler
|
||||
/// functions without holding sched_lock (safe during TDD DL-only slots).
|
||||
extern __thread bool nr_sched_lock_bypassed;
|
||||
|
||||
#define NR_SCHED_ENSURE_LOCKED(lock)\
|
||||
do {\
|
||||
int rc = pthread_mutex_trylock(lock); \
|
||||
AssertFatal(rc == EBUSY, "this function should be called with the scheduler mutex locked, pthread_mutex_trylock() returned %d\n", rc);\
|
||||
if (!nr_sched_lock_bypassed) {\
|
||||
int rc = pthread_mutex_trylock(lock); \
|
||||
AssertFatal(rc == EBUSY, "this function should be called with the scheduler mutex locked, pthread_mutex_trylock() returned %d\n", rc);\
|
||||
}\
|
||||
} while (0)
|
||||
|
||||
/* Commmon */
|
||||
@@ -996,6 +1003,25 @@ typedef struct gNB_MAC_INST_s {
|
||||
|
||||
pthread_mutex_t sched_lock;
|
||||
|
||||
pthread_t L2_tx_thread; ///< MAC DL scheduling thread
|
||||
/// Core affinity for L2 TX (MAC scheduling) thread, -1 = floating
|
||||
int L2_tx_thread_core;
|
||||
|
||||
/// TDD UL pre-scheduling: pre-computed UL DCIs per slot in TDD period.
|
||||
/// Filled by L2_UL_TDD_thread at period start; consumed by L2_tx_thread.
|
||||
nfapi_nr_ul_dci_request_t *ul_precomp_dci;
|
||||
int ul_precomp_period_len;
|
||||
pthread_mutex_t ul_precomp_mutex;
|
||||
pthread_cond_t ul_precomp_start_cond;
|
||||
bool ul_precomp_start_req; ///< DL thread sets, UL thread clears
|
||||
_Atomic int ul_precomp_slots_done; ///< UL thread increments per slot, DL thread reads
|
||||
int ul_precomp_frame; ///< frame of the period being pre-computed
|
||||
int ul_precomp_slot; ///< first slot of the period being pre-computed
|
||||
|
||||
pthread_t L2_UL_TDD_thread; ///< UL pre-scheduling thread (TDD)
|
||||
/// Core affinity for L2 UL pre-scheduling thread, -1 = floating
|
||||
int L2_ul_tdd_thread_core;
|
||||
|
||||
dlul_mac_stats_t mac_stats;
|
||||
uint64_t num_scheduled_prach_rx;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user