Skip to content

OAI L1 + xFAPI + OCUDU L2

This guide covers pairing OCUDU DU-High (L2) with an OAI L1 (PHY) across the FAPI L2-L1 boundary, using xFAPI as the translator-bridge in its OAI_OCUDU mode. OAI L1 speaks nFAPI (open-nFAPI: P5 over SCTP, P7 over UDP); xFAPI translates nFAPI ↔ FAPI and bridges onto xSM shared memory for OCUDU. The deployment is disaggregated: OAI L1 on one host, xFAPI + OCUDU DU-High co-located on another.

For the OCUDU-to-OCUDU split (odu_low instead of OAI), see xFAPI Bridge. This document is the OAI-L1 variant of that flow.

Highlights

  • OAI L1 as a drop-in PHY behind the same odu_high (L2) binary — no L2 code fork; the L1 implementation is a deployment choice.
  • nFAPI ↔ FAPI ↔ xSM translation in xFAPI (OAI_OCUDU mode): nFAPI PNF/VNF on the OAI side, xSM master/slave on the OCUDU side.
  • Disaggregated two-host topology: OAI L1 on Host A, xFAPI + OCUDU DU-High on Host B, joined by an nFAPI (SCTP/UDP) link.
  • OAI-tuned cell config (configs/ocudu_xfapi_oai.yaml) carrying PUCCH/PRACH/power values that match OAI L1's decoders, so a UE attaches end-to-end.
  • Same xSM transport and FAPI serialization as the OCUDU-to-OCUDU bridge — the only thing that changes is the L1 endpoint and xFAPI's mode.

1. Prerequisites

1.1 Hardware

ItemRequirement
CPUx86-64 with AVX2 / AVX-512 (both hosts). Isolated cores for the L1 and xSM RX/TX threads.
NIC (Host A)Fronthaul NIC to the O-RU (e.g. Liteon RU), plus a NIC for the nFAPI SCTP/UDP link to Host B.
NIC (Host B)DPDK-capable NIC for the xSM/DPDK domain, plus a NIC for the nFAPI link to Host A.
SyncPTP grandmaster / GNSS on the L1 host for fronthaul timing.
HugepagesHost B requires hugepages for the DPDK-backed xSM memzone.

1.2 Software

ComponentMinimumNotes
DPDK22.11 (25.11 validated)Host B only (xFAPI + OCUDU). Built with ENABLE_DPDK=True.
Linux kernelPREEMPT_RT recommendedisolcpus for L1 and xSM threads; NUMA pinning on Host B.
Compilergcc/g++ 11+C++17.
OAIOAI L1 nr-softmodem w/ nFAPIBuilt in PNF mode (NFAPI_MODE=PNF), open-nFAPI split transport.
OCUDUthis repo, fapi_split branchENABLE_XSM_FAPI_SPLIT=ON (default).
xFAPIOAI_OCUDU mode buildnFAPI VNF (oai_vnf / oai_p7) + xSM bridge.

1.3 Kernel setup (Host B)

# IOMMU + hugepages on the kernel cmdline (example), then reboot:
#   intel_iommu=on iommu=pt default_hugepagesz=1G hugepagesz=1G hugepages=4 isolcpus=...
sudo modprobe vfio-pci
# Bind the DPDK NIC to vfio-pci (use dpdk-devbind.py for your PCI BDF).

2. Architecture Overview

2.1 Where the split sits

OAI owns the PHY and RU; OCUDU owns MAC and above. The FAPI message set is the contract between them; nFAPI is the wire OAI exposes it on.

2.2 Transport layering

2.3 Topology (disaggregated, two-host)

  • nFAPI wire (Host A ↔ Host B): P5 over SCTP (OAI client → xFAPI VNF listener), P7 over UDP (bidirectional), big-endian TLV, ~1500 B segmentation.
  • xSM wire (inside Host B): xFAPI is xSM master on pair 1's peer side; OCUDU DU-High attaches as xSM master on pair 1, DPDK secondary, file-prefix = gnb0_l2.

3. Implementation Summary

3.1 xSM proxies and gateways

The L2 FAPI adaptor's outbound interfaces are implemented by thin proxies in apps/du/fapi_xsm_proxy.h:

  • xsm_p5_requests_gateway — PARAM / CONFIG / START / STOP requests (L2→L1).
  • xsm_p7_requests_gateway — DL_TTI / UL_TTI / UL_DCI / TX_DATA requests (L2→L1).
  • xsm_p7_indications_notifier — RX_DATA / CRC / UCI / SRS / RACH indications (L1→L2).
  • xsm_p7_slot_indication_notifier — SLOT indication; rotates the UL slot ring each slot.
  • xsm_p5_responses_notifier, xsm_error_indication_notifier, xsm_p7_last_request_notifier (0x8F end-of-slot marker).

Each proxy serializes the message into an xSM buffer and calls xsm_context::put().

3.2 Wire-stable FAPI serialization

Every P5/P7 message has a hand-written serialize() / deserialize() pair under lib/fapi/serialization/ — byte-aligned, length-prefixed for variable arrays, no reflection or RTTI.

Each xSM message is prefixed with a fixed header (fapi_xsm_message_header.h):

FieldSizeMeaning
msg_type1 BFAPI message type ID (see table below)
num_messages_in_block1 Bmessages in this block
msg_len4 Bpayload length after the header
align_offset4 Balignment offset
time_stamp8 BTX/RX timestamp (ns)

Header is ≤ 48 B; XSM_BLOCK_SIZE = 128 KiB per buffer.

FAPI message type IDs (the wire contract):

IDMessageDir
0x00/0x01PARAM.request / responseP5
0x02/0x03CONFIG.request / responseP5
0x04/0x05/0x06START.req / STOP.req / STOP.indP5
0x07ERROR.indicationP5
0x80DL_TTI.requestL2→L1
0x81UL_TTI.requestL2→L1
0x82SLOT.indicationL1→L2
0x83UL_DCI.requestL2→L1
0x84TX_DATA.requestL2→L1
0x85RX_DATA.indicationL1→L2
0x86CRC.indicationL1→L2
0x87UCI.indicationL1→L2
0x88SRS.indicationL1→L2
0x89RACH.indicationL1→L2
0x8FP7 last-message (end of slot)L2→L1

3.3 Single dispatcher thread per process

fapi_xsm_transport runs one receiver thread:

  • Created on start_receiver(); runs receive_loop()dispatch_message() per message.
  • Pinned via pthread_setaffinity_np to rx_cpu (when ≥ 0) at SCHED_FIFO priority rx_priority.
  • dispatch_message() switches on msg_type and routes to the right notifier/gateway.
  • Peer-liveness is tracked (is_peer_alive()); sends are skipped when the peer is down, so an L1 restart does not crash L2.
  • TX path is lock-free: alloc_buffer() → serialize → put(); no kernel copy on the hot path.

4. Clone & Build

Pick a common workspace — this guide uses ~/tossi-ran:

4.1 Clone the repositories

mkdir -p ~/tossi-ran && cd ~/tossi-ran

# OAI L1 (PNF)
git clone https://github.com/TOSSI-Foundation/OAI-RAN/

# OCUDU L1+L2 — the fapi_split branch
git clone -b fapi_split https://github.com/TOSSI-Foundation/OCUDU-RAN/

# xFAPI bridge
git clone https://github.com/TOSSI-Foundation/xFAPI/

4.2 Populate the nFAPI sources in xFAPI

xFAPI does not vendor the nFAPI codec; it is synced in from the OAI checkout. sync_nfapi.sh mirrors the OAI nfapi/ tree into src/ipc/nfapi/ plus a small oai_common/ folder gathered from scattered OAI locations.

cd ~/tossi-ran/xFAPI
./sync_nfapi.sh -v ~/tossi-ran/OAI-RAN
  • -v prints every file copied; -n does a dry run.
  • Destination defaults to src/ipc/nfapi/ (override with NFAPI_DIR).
  • The summary at the end must report 0 missing in OAI.
Note: Re-run sync_nfapi.sh whenever you update the OAI checkout. Recent upstream OAI split the legacy nfapi_p5.c / nfapi_p7.c into nfapi_nr_p5.c / nfapi_nr_p7.c (+ nfapi_lte_p7.c for the generic P7 header helpers); CMakeLists.txt is already wired for this layout.

4.3 Build xFAPI

cd ~/tossi-ran/xFAPI
source ./setup_env.sh
./build_xfapi.sh --mode=oai_ocudu
  • setup_env.sh exports the required environment (incl. DPDK_PATH).
  • Use ./build_xfapi.sh --clean first for a clean rebuild.
  • Produces bin/xfapi_main.

4.4 Build OCUDU

cd ~/tossi-ran/OCUDU-RAN
mkdir -p build && cd build
sudo cmake -DDU_SPLIT_TYPE=SPLIT_7_2 \
           -DENABLE_DPDK=True \
           -DASSERT_LEVEL=MINIMAL \
           -DENABLE_UHD=OFF ../
make -j$(nproc)

Produces the ocu (CU) and odu_high (DU/L2) binaries.

4.5 Build OAI (gNB L1 / PNF)

cd ~/tossi-ran/OAI-RAN/cmake_targets

# One-time: install build dependencies
sudo ./build_oai -I

# Build the gNB with the O-RAN 7.2 fronthaul library
sudo ./build_oai --gNB --ninja -t oran_fhlib_5g \
     --cmake-opt -Dxran_LOCATION=$HOME/phy/shi_lib/lib
Adjust -Dxran_LOCATION to wherever your xRAN library is built.

4.6 xSM verification

ldd ~/tossi-ran/OCUDU-RAN/build/apps/du/odu_high | grep xsm   # libxsm.so resolved

5. Configuration

5.1 Key knobs

OCUDU L2 side (configs/ocudu_xfapi_oai.yaml):

fapi_split_l2:
  rx_cpu: 30                 # SCHED_FIFO RX thread affinity (-1 = no pinning)
  rx_priority: 85            # [1, 99]
  xsm_device_name: xsm_bridge
  xsm_pair_index: 1          # OCUDU attaches as master on pair 1
  xsm_file_prefix: gnb0_l2   # must match xFAPI-L2's DPDK file-prefix
  dpdk_proc_type: secondary  # xFAPI owns the primary

5.2 OAI-tuned cell config

These cell_cfg values in ocudu_xfapi_oai.yaml exist specifically so OCUDU's MAC stays on OAI L1's tested decode paths:

SettingValueWhy (OAI L1 constraint)
pucch.formatsf0_and_f2OAI supports PUCCH format 0/2 only.
pucch.f2_max_nof_rbs4Bounds the F2 grant; aligns with mult-of-4 PRB rule.
prach.preamble_rx_target_pw-120Avoid O-RU RX saturation of nearby UEs.
pusch.p0_nominal_with_grant-96UL power target for OAI.
band / bw / scsn78 / 100 MHz / 30 kHzValidated TDD carrier.

Three source-side fixes back these values (already in the L2 build):

5.3 nFAPI socket endpoints (Host A OAI ↔ Host B xFAPI)

PlaneTransportEndpoint
P5SCTPOAI client → xFAPI VNF listener (e.g. :50001)
P7UDPbidirectional (e.g. 50010 ↔ 50011)

The OAI nFAPI config and xFAPI's VNF config must agree on these ports.


6. Run

Start the components in this order, each in its own terminal. xFAPI must own its DPDK/xSM resources before the DU attaches; the L1 must be listening before the DU drives slots.

6.1 xFAPI bridge (Host B, Terminal 1)

cd ~/tossi-ran/xFAPI
./run_xfapi.sh

This creates the xSM memzone (DPDK primary, file-prefix gnb0_l2) and opens the nFAPI P5 SCTP + P7 UDP listeners (VNF).

6.2 OCUDU CU (Host B, Terminal 2)

cd ~/tossi-ran/OCUDU-RAN/build/<cu_bin_dir>
./ocu -c ../../../configs/cu.yml

6.3 OAI L1 / PNF (Host A)

cd ~/tossi-ran/OAI-RAN/cmake_targets/ran_build/build
sudo NFAPI_TRACE_LEVEL=info ./nr-softmodem \
     -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-pnf.band78.liteon.conf \
     --nfapi PNF

The nFAPI PNF connects to xFAPI over P5 SCTP; PARAM/CONFIG exchanged; SLOT.indication starts ticking.

6.4 OCUDU DU / L2 (Host B, Terminal 3)

cd ~/tossi-ran/OCUDU-RAN/build/<odu_bin_dir>
./odu_high -c ../../../configs/odu_high_xfapi.yaml

Once all four are up, OAI L1 (PNF) and OCUDU L2 (VNF) are connected through xFAPI: nFAPI P5 (PARAM/CONFIG/START) over SCTP, then P7 (SLOT / DL_TTI / UL_TTI / TX_DATA and the RX/CRC/UCI/RACH indications) over UDP.

6.5 Startup verification

ProcessExpect
xFAPInFAPI P5 handshake to RUNNING; xSM memzone created; P7 segmenter ready.
OCUDU CUF1AP listener up; waiting for DU F1 Setup.
OAI L1nFAPI PNF connected; PARAM/CONFIG exchanged; SLOT.indication ticking.
OCUDU DU[OCUDU-CFG] CONFIG.request emitted; cell up; F1AP/RRC to CU established; UE attach completes.

6.6 Troubleshooting

  • Cannot find source file ... nfapi_p5.c at CMake configure: the synced tree is from a newer OAI that renamed the codec sources — re-run sync_nfapi.sh and rebuild; CMakeLists.txt expects the NR-split layout.
  • undefined reference to nfapi_p7_message_header_unpack at link: ensure nfapi_lte_p7.c is present in the synced tree (it carries the generic P7 header helpers after the upstream split).
  • DPDK errors: confirm DPDK_PATH is set (via setup_env.sh) and hugepages are configured.
  • Order matters: if xFAPI or CU are not ready when the DU starts, the F1 Setup or xSM attach will fail — always follow the sequence in §6.1–6.4.

7. Metrics & Observability

7.1 FAPI stats recorder

metrics:
  enable_verbose: true
  autostart_stdout_metrics: true

An optional in-memory FAPI message recorder captures per-message type/direction/size/IPC-latency and dumps JSON at shutdown — useful to diff OCUDU's emitted PDUs against OAI's expectations.

7.2 Logging

log:
  filename: odu_high.log
  all_level: debug    # bring-up; lower to warning in production

The L2 send path also emits throttled [OCUDU-CFG] / MIB / TB fingerprints for byte-for-byte comparison against xFAPI and OAI dumps during interop debugging.


8. Deployment Checklist

  • Host B: IOMMU on, hugepages reserved, DPDK NIC bound to vfio-pci.
  • Host A: O-RU fronthaul up, PTP/GNSS locked.
  • nFAPI link reachable both ways; P5 SCTP + P7 UDP ports match between OAI and xFAPI.
  • xFAPI built in OAI_OCUDU mode; libxsm.so + xSM headers placed in the OCUDU tree.
  • OCUDU built with ENABLE_XSM_FAPI_SPLIT=ON, ENABLE_DPDK=True.
  • ocudu_xfapi_oai.yaml: xsm_file_prefix = gnb0_l2, xsm_pair_index = 1, dpdk_proc_type = secondary.
  • RX thread rx_cpu / rx_priority on an isolated SCHED_FIFO core.
  • Startup order: xFAPI → OAI L1 → OCUDU L2.
  • Verify CONFIG exchange, cell up, F1AP to CU, UE attach.

9. References

  • xFAPI — https://github.com/coranlabs/xFAPI
  • OCUDU-to-OCUDU bridge — /docs/ran-integration/fapi-split/xfapi-bridge/
  • FAPI split topologies — docs/fapi_split_topologies.md
  • OpenAirInterface (OAI) — https://gitlab.eurecom.fr/oai/openairinterface5g
  • DPDK — https://www.dpdk.org/
  • SCF 222.10 (5G FAPI: PHY API), 3GPP TS 38.211/212/213/214 (PUCCH/PRACH).