9. Top Level PHY RAN Application#

This notebook provides a PHY RAN App integration test where we emulate the surrounding components (MAC Layer and Radio Unit) to isolate and test the PHY layer performance.

Key Features:

  1. Background: Overview of MAC & RU Emulators and PHY RAN App.

  2. System Overview: Architectural and communication highlights.

  3. Setup and Build: Build of components for the integration test.

  4. Test and Results: Running the test and interpreting output/results.

Prerequisites:

  • GH200 server with BF3 NIC configured in loopback mode

  • CPU core isolation for real-time performance

  • Execution inside the Docker container

Time: ~10 minutes

Step 1: Understand System Architecture#

To test the PUSCH receiver pipeline from previous tutorials, we need to emulate the surrounding components:

  • testMAC: Emulates the MAC, sending scheduling commands (FAPI) via Shared Memory (nvIPC).

  • phy_ran_app: The System Under Test. It runs the full PUSCH (Uplink) receiver pipeline.

  • ru_emulator: Emulates the Radio Unit, exchanging IQ samples via Fronthaul (DPDK/DOCA).

A simplified 5G network deployment may look like:

Real System Architecture

We want to simulate these components using the same hardware as that used in real deployments. In this tutorial, we use a GH200 server and BF3 NIC configured in loopback mode. The software processes are executed inside a container as shown below:

PHY App Container Architecture

Step 2: Explore Communication Flow (FAPI and Fronthaul)#

The interaction between these components is driven by strict timing and protocol standards. Below is a simplified diagram of the FAPI and Fronthaul data flow:

Communication Flow

Communication Interfaces#

  1. Northbound (MAC-PHY):

    • Protocol: FAPI (Functional Application Platform Interface)

    • Transport: nvIPC (Shared Memory)

    • Messages: UL_TTI_REQUEST (Schedule this slot), SLOT.indication (Time tick)

  2. Southbound (RU):

    • Protocol: O-RAN Fronthaul (7.2 Split)

    • Transport: Ethernet (DPDK for Control, DOCA GPUNetIO for Data)

    • Messages: C-Plane (Control info), U-Plane (IQ Data)

Data Flow#

The phy_ran_app doesn’t just forward messages. It translates them:

  • Receives a FAPI Request from the MAC (“Decode user X at freq Y”).

  • Translates this into C-Plane Messages for the Radio (“Prepare to receive on PRB Z”).

  • Waits for U-Plane Data from the Radio (IQ samples).

  • Runs the Signal Processing Pipeline (PUSCH).

  • Sends the transport block and CRC back to the MAC

And the testMAC subsequently compares the received bits with the expected bits to verify the correctness of the PUSCH receiver pipeline. It additionally checks for CRC pass/fail and other measurements like RSSI and SINR.

Step 3: Review PUSCH Pipeline#

The phy_ran_app runs the PUSCH receiver, which contains the Python-lowered inner receiver and CUDA-based outer receiver from earlier tutorials. Below is a simplified diagram of the PUSCH receiver pipeline:

Simple PUSCH Receiver Pipeline

Step 4: Configure#

We will now configure the build system for phy_ran_app. This process uses CMake with a release preset to ensure optimal performance for real-time execution.

[ ]:
import os

# Import shared tutorial utilities from tutorial_utils.py
from tutorial_utils import (
    build_cmake_target,
    check_container_running,
    check_network_devices,
    configure_cmake,
    get_project_root,
    is_running_in_docker,
    run_container_command,
    show_output,
)

IN_DOCKER = is_running_in_docker()
PROJECT_ROOT = get_project_root()
CONTAINER_NAME = f"aerial-framework-base-{os.environ.get('USER', 'default')}"

# Ensure the container is running
print(f"Project root: {PROJECT_ROOT}")
if IN_DOCKER:
    print("✅ Running inside Docker container")
else:
    print(f"Running on host, will use container: {CONTAINER_NAME}")
    check_container_running(CONTAINER_NAME)
    print(f"✅ Container '{CONTAINER_NAME}' is running")
print("✅ Step 4a complete: Environment setup verified")
Project root: /opt/nvidia/aerial-framework
✅ Running inside Docker container
✅ Step 4a complete: Environment setup verified

Configure CMake:

[ ]:
# Configure CMake with preset
preset = "gcc-release"
print(f"Configuring {preset}...")

configure_cmake(PROJECT_ROOT / f"out/build/{preset}", preset=preset)
print("✅ Step 4b complete: CMake configured")
Configuring gcc-release...
✅ Step 4b complete: CMake configured

Step 5: Build the Target#

Now we compile the application and its dependencies.

We build phy_ran_app and its dependencies, which includes testMAC and ru_emulator.

[ ]:
print(f"Building phy_ran_app with {preset} preset...")

build_cmake_target(PROJECT_ROOT / f"out/build/{preset}", "phy_ran_app")
print("✅ Step 5 complete: PHY RAN app built")
Building phy_ran_app with gcc-release preset...
Building phy_ran_app...
✓ phy_ran_app ready
✅ Step 5 complete: PHY RAN app built

Step 6: Run Integration Test#

The integration test launches the full trio: ru_emulator (background), testMAC (background), and phy_ran_app (foreground).

It validates that:

  1. Connections are established.

  2. Packets flow from RU to DU.

  3. The pipeline processes data without errors.

  4. Timing constraints are met.

[ ]:
def _print_integration_summary(log: str) -> None:
    checks = {
        "CRC PASS found": "crc=PASS" in log,
        "ru_emulator exited cleanly": "INFO: ru_emulator exited cleanly" in log,
        "test_mac exited cleanly": "INFO: test_mac exited cleanly" in log,
        "phy_ran_app exit code 0": "INFO: phy_ran_app exited with code: 0" in log,
        "Integration test completed": "INFO: Integration test completed successfully" in log,
        "ctest 100% tests passed": "100% tests passed" in log,
    }

    overall_ok = all(checks.values())
    line = "=" * 60

    print(line)
    print("PHY RAN APP INTEGRATION SUMMARY")
    print(line)
    for label, ok in checks.items():
        print(f"{'✅' if ok else '❌'} {label}")
    print(line)
    print("✅ OVERALL: PASS" if overall_ok else "❌ OVERALL: FAIL")
    print(line)


# Check networking availability
if check_network_devices(CONTAINER_NAME):
    print("Running phy_ran_app integration test...")

    # Run the integration test (default: 100 slots)
    cmd = f"ctest --preset {preset} -R phy_ran_app.integration_test"
    result = run_container_command(cmd, CONTAINER_NAME, cwd=PROJECT_ROOT)

    show_output(result, lines=20)
    _print_integration_summary(result.stdout)
    success = result.returncode == 0
    print("✅ Integration test passed" if success else "⚠️  Integration test failed")
    print("✅ Step 6 complete: Integration test executed")
✅ Networking devices detected
Running phy_ran_app integration test...
Running: ctest --preset gcc-release -R phy_ran_app.integration_test
  104: Exiting bg_fmtlog_collector - log queue ever was full: 0
  104: INFO: ru_emulator exited cleanly
  104: INFO: Integration test completed successfully
  4/4 Test #104: phy_ran_app.integration_test ...............   Passed   34.09 sec

  The following tests passed:
        ran_build_setup
        ran_sync_env_python
        py_ran_test_pusch_inner_receiver_fixture
        phy_ran_app.integration_test

  100% tests passed, 0 tests failed out of 4

  Label Time Summary:
  integration     =  34.09 sec*proc (1 test)
  parallel        =   3.19 sec*proc (3 tests)
  real_time       =  34.09 sec*proc (1 test)
  requires_nic    =  34.09 sec*proc (1 test)

  Total Test time (real) =  37.29 sec
============================================================
PHY RAN APP INTEGRATION SUMMARY
============================================================
✅ CRC PASS found
✅ ru_emulator exited cleanly
✅ test_mac exited cleanly
✅ phy_ran_app exit code 0
✅ Integration test completed
✅ ctest 100% tests passed
============================================================
✅ OVERALL: PASS
============================================================
✅ Integration test passed
✅ Step 6 complete: Integration test executed

Understanding the Output#

  • On-time Packets: Indicates that the phy_ran_app is processing data fast enough to keep up with the 5G slot schedule.

  • Late Packets: If these appear, it often indicates system tuning issues (e.g., CPU isolation not configured, debug build used instead of release).

  • CRC Pass/Fail: The test vector contains known data. A “Pass” means the PUSCH pipeline successfully recovered the original bits.

For more information on the integration test, see the Real-Time Applications documentation.

Next Steps#

Now that you have verified the core PHY application:

  • Performance Profiling: Use Nsight Systems to visualize the GPU kernel execution timeline.

  • Custom Test Vectors: Try running with different TEST_VECTOR files to test different MCS (Modulation and Coding Schemes).

  • Test Slots: Configure TEST_SLOTS=<num> to control how many slots are tested. See PHY RAN App API Reference for more details.

  • CTest Timeout for Long Tests: For long-running tests with large TEST_SLOTS values, CTest’s default timeout (1500s) may be insufficient. Use --timeout to increase it: TEST_SLOTS=200000 ctest --preset gcc-release --timeout 3000 -R phy_ran_app.integration_test