Compare commits

...

2 Commits

Author SHA1 Message Date
Gabriele Gemmi
0988366ad2 fapi/aerial: P5 bootstrap infrastructure for multi-cell support
Add `num_phys` to `nvipc_params_t` so the number of configured PHYs
flows from `gnb_config.c` down into the nvIPC layer without hardcoding.

`configure_nr_nfapi_vnf` now creates one `pnf_list` entry per PHY
(instead of a single hardcoded entry), and `epoll_recv_task` injects
one fake PARAM.RESPONSE per PHY so each cell triggers its own
CONFIG.REQUEST.  `nr_param_resp_cb` picks up `config[p5_idx]` so the
correct per-cell MAC config is sent.

Incoming P5 responses from cuBB are dispatched using `msg->cell_id` as
`p5_idx` in `vnf_nr_handle_p4_p5_message()`, matching the per-cell
routing established for P7.

Single-cell behaviour is unchanged (`num_phys = 1`).

Signed-off-by: Gabriele Gemmi <g.gemmi@northeastern.edu>
2026-05-08 04:51:32 +00:00
Gabriele Gemmi
375811a8c6 fapi/aerial: per-cell P7 routing and unified 0-based phy_id indexing
P7 send functions (oai_fapi_ul_tti_req, oai_fapi_ul_dci_req,
oai_fapi_tx_data_req, oai_fapi_dl_tti_req, oai_fapi_send_end_request)
now take a phy_id argument instead of a hardcoded value, resolving the
TODO FIXME stubs. The parameter is renamed from CC_id to phy_id to
reflect that it is a FAPI-layer identifier, not a MAC-layer concept.

On the P5 send path, CONFIG.REQUEST cell_id is derived from
msg->phy_id rather than hardcoded to 0.

On the receive side, slot indication tracking (old_sfn/old_slot) is
promoted from static scalars to NFAPI_CC_MAX arrays indexed by
msg->cell_id, renamed to phy_id consistently. cuBB does not populate
ind.header.phy_id reliably in incoming messages; it is set from the
transport-level cell_id instead.

All phy_id and cell_id values are now consistently 0-based across the
aerial boundary, eliminating all ±1 conversions:

- nr_param_resp_cb: req->header.phy_id = p5_idx (was phy->id).
  Fake PARAM.RESPONSE injection uses p5_idx=0 for the first cell.
- phy_nr_slot_indication: CC_id = ind->header.phy_id directly,
  no guard or subtraction.
- fapi_nvIPC.c: cell_id = msg->phy_id with no subtraction.
- All P7 send functions: header.phy_id = phy_id with no +1.

For multi-cell, injecting p5_idx=0,1,2,... maps directly to
phy_id=0,1,2,... and cell_id=0,1,2,... with no conversion at any
boundary.

Signed-off-by: Gabriele Gemmi <g.gemmi@northeastern.edu>
2026-04-30 18:34:17 +00:00
5 changed files with 66 additions and 62 deletions

View File

@@ -59,8 +59,9 @@ void nvIPC_send_stop_request()
}
static uint16_t old_sfn = 0;
static uint16_t old_slot = 0;
static uint16_t old_sfn[NFAPI_PHY_MAX];
static uint16_t old_slot[NFAPI_PHY_MAX];
static nvipc_params_t aerial_params;
////////////////////////////////////////////////////////////////////////
// Handle an RX message
static int ipc_handle_rx_msg(nv_ipc_msg_t *msg)
@@ -88,7 +89,7 @@ static int ipc_handle_rx_msg(nv_ipc_msg_t *msg)
vnf_p7_t *vnf_p7_config = (vnf_p7_t *)((vnf_info *)vnf_config->user_data)->p7_vnfs->config;
switch (fapi_msg.message_id) {
case NFAPI_NR_PHY_MSG_TYPE_PARAM_RESPONSE ... NFAPI_NR_PHY_MSG_TYPE_ERROR_INDICATION:
vnf_nr_handle_p4_p5_message(msg->msg_buf, msg->msg_len, 1, vnf_config);
vnf_nr_handle_p4_p5_message(msg->msg_buf, msg->msg_len, msg->cell_id, vnf_config);
break;
// P7 Messages
case NFAPI_NR_PHY_MSG_TYPE_RX_DATA_INDICATION: {
@@ -134,22 +135,28 @@ static int ipc_handle_rx_msg(nv_ipc_msg_t *msg)
NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s: Failed to unpack message\n", __FUNCTION__);
} else {
NFAPI_TRACE(NFAPI_TRACE_DEBUG, "%s: Handling NR SLOT Indication\n", __FUNCTION__);
// use transport-level cell_id (0-based) from the nvIPC header —
// ind.header.phy_id is not reliably populated by cuBB in incoming messages;
// normalize it here so downstream callbacks can rely on it
uint8_t phy_id = msg->cell_id;
AssertFatal(phy_id < NFAPI_PHY_MAX, "phy_id %d exceeds NFAPI_PHY_MAX %d\n", phy_id, NFAPI_PHY_MAX);
ind.header.phy_id = phy_id;
// check if the sfn/slot unpacked come wrong at any time, should be old + 1 (slot 0 -- 19, sfn 0 -- 1023)
// add 1 to current sfn number
uint16_t old_slot_plus = ((old_slot + 1) % 20);
uint16_t old_sfn_plus = old_slot_plus == 0 ? ((old_sfn + 1) % 1024) : old_sfn;
uint16_t old_slot_plus = ((old_slot[phy_id] + 1) % 20);
uint16_t old_sfn_plus = old_slot_plus == 0 ? ((old_sfn[phy_id] + 1) % 1024) : old_sfn[phy_id];
if (old_slot_plus != ind.slot || old_sfn_plus != ind.sfn) {
LOG_E(NFAPI_VNF,
"\n============================================================================\n"
"sfn slot doesn't match unpacked one! L2->L1 %d.%d vs L1->L2 %d.%d \n"
"sfn slot doesn't match unpacked one! PHY %d L2->L1 %d.%d vs L1->L2 %d.%d \n"
"============================================================================\n",
old_sfn,
old_slot,
phy_id,
old_sfn[phy_id],
old_slot[phy_id],
ind.sfn,
ind.slot);
}
old_sfn = ind.sfn;
old_slot = ind.slot;
old_sfn[phy_id] = ind.sfn;
old_slot[phy_id] = ind.slot;
if (vnf_p7_config->_public.nr_slot_indication) {
(vnf_p7_config->_public.nr_slot_indication)(&ind);
}
@@ -210,7 +217,7 @@ bool aerial_nr_send_p5_message(vnf_t *vnf, uint16_t p5_idx, nfapi_nr_p4_p5_messa
if (pnf) {
// Create the message
nv_ipc_msg_t send_msg = {.msg_id = msg->message_id,
.cell_id = 0,
.cell_id = msg->phy_id,
// By default, P5 uses only message pool.
.data_pool = NV_IPC_MEMPOOL_CPU_MSG,
.data_len = 0,
@@ -329,11 +336,12 @@ void *epoll_recv_task(void *arg)
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1) {
LOG_E(NFAPI_VNF, "%s epoll_ctl failed\n", __func__);
}
// From here on out the thread is ready to receive data, simulate the reception
// of a PARAM.response to get the VNF to send a CONFIG.request
// Simulate one PARAM.response per configured PHY to trigger a CONFIG.request
// for each cell. aerial_params is populated by nvIPC_Init before this thread starts.
nfapi_nr_param_response_scf_t resp_msg = {.header.message_id = NFAPI_NR_PHY_MSG_TYPE_PARAM_RESPONSE};
nfapi_vnf_config_t * vnf_config = get_config();
vnf_config->nr_param_resp(vnf_config, 1, &resp_msg);
nfapi_vnf_config_t *vnf_config = get_config();
for (int i = 0; i < aerial_params.num_phys; i++)
vnf_config->nr_param_resp(vnf_config, i, &resp_msg);
while (((vnf_t *)vnf_config)->terminate == false) {
LOG_D(NFAPI_VNF, "%s: epoll_wait fd_rx=%d ...\n", __func__, ipc_rx_event_fd);
@@ -405,16 +413,17 @@ int nvIPC_Init(nvipc_params_t nvipc_params_s)
return -1;
}
LOG_I(NFAPI_VNF, "%s: create IPC interface successful\n", __func__);
aerial_params = nvipc_params_s;
sleep(1);
create_recv_thread(nvipc_params_s.nvipc_poll_core);
return 0;
}
int oai_fapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req)
int oai_fapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req, uint8_t phy_id)
{
nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config();
ul_tti_req->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!!
ul_tti_req->header.phy_id = phy_id;
ul_tti_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_UL_TTI_REQUEST;
bool retval = p7_config->send_p7_msg(get_p7_vnf(), &ul_tti_req->header);
@@ -431,10 +440,10 @@ int oai_fapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req)
return retval;
}
int oai_fapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req)
int oai_fapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req, uint8_t phy_id)
{
nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config();
ul_dci_req->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!!
ul_dci_req->header.phy_id = phy_id;
ul_dci_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_UL_DCI_REQUEST;
bool retval = p7_config->send_p7_msg(get_p7_vnf(), &ul_dci_req->header);
@@ -446,10 +455,10 @@ int oai_fapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req)
return retval;
}
int oai_fapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req)
int oai_fapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req, uint8_t phy_id)
{
nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config();
tx_data_req->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!!
tx_data_req->header.phy_id = phy_id;
tx_data_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_TX_DATA_REQUEST;
bool retval = p7_config->send_p7_msg(get_p7_vnf(), &tx_data_req->header);
@@ -462,11 +471,11 @@ int oai_fapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req)
return retval;
}
int oai_fapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req)
int oai_fapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req, uint8_t phy_id)
{
nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config();
dl_config_req->header.message_id = NFAPI_NR_PHY_MSG_TYPE_DL_TTI_REQUEST;
dl_config_req->header.phy_id = 1; // DJP HACK TODO FIXME - need to pass this around!!!!
dl_config_req->header.phy_id = phy_id;
bool retval = p7_config->send_p7_msg(get_p7_vnf(), &dl_config_req->header);
dl_config_req->dl_tti_request_body.nPDUs = 0;
@@ -478,10 +487,11 @@ int oai_fapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req)
return retval;
}
int oai_fapi_send_end_request(uint32_t frame, uint32_t slot)
int oai_fapi_send_end_request(uint32_t frame, uint32_t slot, uint8_t phy_id)
{
nfapi_vnf_p7_config_t *p7_config = get_p7_vnf_config();
nfapi_nr_slot_indication_scf_t nr_slot_resp = {.header.message_id = 0x8F, .sfn = frame, .slot = slot};
nfapi_nr_slot_indication_scf_t nr_slot_resp = {
.header.message_id = 0x8F, .header.phy_id = phy_id, .sfn = frame, .slot = slot};
bool retval = p7_config->send_p7_msg(get_p7_vnf(), &nr_slot_resp.header);
if (!retval) {

View File

@@ -17,6 +17,12 @@
#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h"
/* Maximum number of PHY instances supported by this layer.
* Aliases NFAPI_CC_MAX which itself tracks NFAPI_MAX_CC; using
* NFAPI_PHY_MAX here makes clear that this is a per-PHY (not
* per-carrier-component) limit at the cuBB transport boundary. */
#define NFAPI_PHY_MAX NFAPI_CC_MAX
int get_cpu_msg_buf_size();
int get_cpu_data_buf_size();
bool allocate_msg(nv_ipc_msg_t* send_msg);
@@ -24,11 +30,11 @@ void release_msg(nv_ipc_msg_t* send_msg);
bool send_nvipc_msg(nv_ipc_msg_t* send_msg);
bool aerial_nr_send_p5_message(vnf_t *vnf, uint16_t p5_idx, nfapi_nr_p4_p5_message_header_t *msg, uint32_t msg_len);
int nvIPC_Init(nvipc_params_t nvipc_params_s);
int oai_fapi_send_end_request(uint32_t frame, uint32_t slot);
int oai_fapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req);
int oai_fapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req);
int oai_fapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req);
int oai_fapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req);
int oai_fapi_send_end_request(uint32_t frame, uint32_t slot, uint8_t PHY_id);
int oai_fapi_ul_tti_req(nfapi_nr_ul_tti_request_t *ul_tti_req, uint8_t PHY_id);
int oai_fapi_ul_dci_req(nfapi_nr_ul_dci_request_t *ul_dci_req, uint8_t PHY_id);
int oai_fapi_tx_data_req(nfapi_nr_tx_data_request_t *tx_data_req, uint8_t PHY_id);
int oai_fapi_dl_tti_req(nfapi_nr_dl_tti_request_t *dl_config_req, uint8_t PHY_id);
void nvIPC_Stop();
void nvIPC_send_stop_request();
#endif // OPENAIRINTERFACE_FAPI_NVIPC_H

View File

@@ -897,25 +897,26 @@ int phy_nr_slot_indication(nfapi_nr_slot_indication_scf_t *ind)
ifi->NR_slot_indication(ind, &sched_response);
#ifdef ENABLE_AERIAL
uint8_t PHY_id = ind->header.phy_id;
bool send_slt_resp = false;
if (sched_response.DL_req.dl_tti_request_body.nPDUs> 0) {
oai_fapi_dl_tti_req(&sched_response.DL_req);
oai_fapi_dl_tti_req(&sched_response.DL_req, PHY_id);
send_slt_resp = true;
}
if (sched_response.UL_tti_req.n_pdus > 0) {
oai_fapi_ul_tti_req(&sched_response.UL_tti_req);
oai_fapi_ul_tti_req(&sched_response.UL_tti_req, PHY_id);
send_slt_resp = true;
}
if (sched_response.TX_req.Number_of_PDUs > 0) {
oai_fapi_tx_data_req(&sched_response.TX_req);
oai_fapi_tx_data_req(&sched_response.TX_req, PHY_id);
send_slt_resp = true;
}
if (sched_response.UL_dci_req.numPdus > 0) {
oai_fapi_ul_dci_req(&sched_response.UL_dci_req);
oai_fapi_ul_dci_req(&sched_response.UL_dci_req, PHY_id);
send_slt_resp = true;
}
if (send_slt_resp) {
oai_fapi_send_end_request(ind->sfn, ind->slot);
oai_fapi_send_end_request(ind->sfn, ind->slot, PHY_id);
}
#else
if (sched_response.DL_req.dl_tti_request_body.nPDUs > 0)
@@ -1365,7 +1366,8 @@ int nr_param_resp_cb(nfapi_vnf_config_t *config, int p5_idx, nfapi_nr_param_resp
vnf_p7_info *p7_vnf = vnf->p7_vnfs;
pnf_info *pnf = vnf->pnfs;
phy_info *phy = pnf->phys;
nfapi_nr_config_request_scf_t *req = &RC.nrmac[0]->config[0]; // check
phy->id = p5_idx;
nfapi_nr_config_request_scf_t *req = &RC.nrmac[0]->config[p5_idx];
#ifndef ENABLE_AERIAL
struct sockaddr_in pnf_p7_sockaddr;
phy->remote_port = resp->nfapi_config.p7_pnf_port.value;
@@ -1780,33 +1782,17 @@ void configure_nr_nfapi_vnf(eth_params_t params)
config->pack_func = &fapi_nr_p5_message_pack;
config->send_p5_msg = &aerial_nr_send_p5_message;
NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] Created VNF NFAPI start thread %s\n", __FUNCTION__);
nfapi_vnf_pnf_info_t *pnf = (nfapi_vnf_pnf_info_t *)malloc(sizeof(nfapi_vnf_pnf_info_t));
NFAPI_TRACE(NFAPI_TRACE_INFO, "MALLOC nfapi_vnf_pnf_info_t for pnf_list pnf:%p\n", pnf);
memset(pnf, 0, sizeof(nfapi_vnf_pnf_info_t));
pnf->p5_idx = 1;
pnf->connected = 1;
// Add needed parameters
pnf_info *pnf_info = vnf->pnfs;
for (int i = 0; i < 1; ++i) {
phy_info phy;
memset(&phy, 0, sizeof(phy));
phy.index = 0;
NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] (PHY:%d) phy_config_idx:%d\n", i, 0);
nfapi_vnf_allocate_phy(config, 1, &(phy.id));
for (int j = 0; j < 1; ++j) {
NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] (PHY:%d) (RF%d) %d\n", i, j, 0);
phy.rfs[0] = 0;
}
pnf_info->phys[0] = phy;
// One pnf list entry per configured PHY so that aerial_nr_send_p5_message()
// can route CONFIG/START requests by phy_id.
uint8_t num_phys = RC.nrmac[0]->nvipc_params_s.num_phys;
for (int i = 0; i < num_phys; i++) {
nfapi_vnf_pnf_info_t *pnf = calloc(1, sizeof(*pnf));
pnf->p5_idx = i;
pnf->connected = 1;
nfapi_vnf_pnf_list_add(config, pnf);
NFAPI_TRACE(NFAPI_TRACE_INFO, "Registered aerial PNF entry for phy_id %d\n", i);
}
nfapi_vnf_pnf_list_add(config, pnf);
vnf_p7_info *p7_vnf = vnf->p7_vnfs;
NFAPI_TRACE(NFAPI_TRACE_INFO,

View File

@@ -1699,6 +1699,7 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg)
nvipc_params_t nvipc_p = {
.nvipc_shm_prefix = strdup(*gpd(params, np, MACRLC_TRANSPORT_S_SHM_PREFIX)->strptr),
.nvipc_poll_core = *gpd(params, np, MACRLC_TRANSPORT_S_POLL_CORE)->i8ptr,
.num_phys = 1,
};
RC.nrmac[j]->nvipc_params_s = nvipc_p;
LOG_I(GNB_APP, "Configuring VNF for Aerial connection with prefix %s\n", nvipc_p.nvipc_shm_prefix);

View File

@@ -862,6 +862,7 @@ typedef struct {
typedef struct {
char *nvipc_shm_prefix;
int8_t nvipc_poll_core;
uint8_t num_phys;
} nvipc_params_t;
typedef struct {