FAPI#

5G Small Cell Forum (SCF) FAPI interface implementation for PHY-MAC communication with message capture and replay capabilities.

Overview#

The FAPI module provides a complete implementation of the 5G NR FAPI interface for communication between PHY and MAC layers. It includes state machine management, message routing over NVIPC transport, and file-based capture/replay for testing.

Key Features#

  • State Machine Management: Per-cell state tracking (Idle → Configured → Running → Stopped)

  • NVIPC Transport: High-performance shared memory message passing with RAII lifecycle management

  • Message Routing: Automatic dispatch of CONFIG, START, STOP requests with response generation

  • File Capture/Replay: Record and replay FAPI message sequences for deterministic testing

  • Callback System: User-configurable handlers for UL_TTI_REQUEST, DL_TTI_REQUEST, and other messages

  • Thread Safety: Lock-free slot management and thread-safe message operations

Core Concepts#

FAPI State Machine#

FapiState manages the complete FAPI message lifecycle over NVIPC transport. Each cell maintains its own state that transitions through the FAPI state machine based on received messages.

The NVIPC configuration can be provided either as a file path or inline YAML string. The state machine initializes transport endpoints and allocates per-cell state structures.

State Initialization#

// Configure FAPI state machine
ran::fapi::FapiState::InitParams params{};
params.nvipc_config_string =
        ran::fapi::YamlConfigBuilder::create_primary_config("fapi_sample_init");
params.max_cells = 4;
params.max_sfn = 1024;
params.max_slot = 20;

// Initialize FAPI state
const ran::fapi::FapiState fapi_state(params);

Slot Management#

// Reset slot to (0, 0)
fapi_state.reset_slot();

// Get current slot
const auto slot = fapi_state.get_current_slot();

// Advance to next slot
fapi_state.increment_slot();

const auto next_slot = fapi_state.get_current_slot();

Slot management uses lock-free atomics for thread-safe operation. The slot counter automatically wraps around at configured SFN and slot limits.

Message Callbacks#

FapiState provides callbacks for handling different message types. Callbacks are invoked during message processing and can be used to forward messages to application logic.

Basic Message Callbacks#

std::size_t message_count{};

// Set callback to capture all messages
fapi_state.set_on_message(
        [&message_count](const ran::fapi::FapiMessageData & /*msg*/) { message_count++; });

// Set callback for UL TTI requests
fapi_state.set_on_ul_tti_request([](std::uint16_t /*cell_id*/,
                                    const scf_fapi_body_header_t & /*body_hdr*/,
                                    std::uint32_t /*body_len*/) {
    // Process UL TTI request
});

// Set callback for DL TTI requests
fapi_state.set_on_dl_tti_request([](std::uint16_t /*cell_id*/,
                                    const scf_fapi_body_header_t & /*body_hdr*/,
                                    std::uint32_t /*body_len*/) {
    // Process DL TTI request
});

The on_message callback captures all messages before routing to specific handlers, useful for logging or recording.

Configuration Callbacks#

// Set callback for CONFIG.request
fapi_state.set_on_config_request(
        [](std::uint16_t /*cell_id*/,
           const scf_fapi_body_header_t & /*body_hdr*/,
           std::uint32_t /*body_len*/) -> scf_fapi_error_codes_t {
            // Validate configuration
            return static_cast<scf_fapi_error_codes_t>(SCF_FAPI_MSG_OK);
        });

// Set callback for START.request
fapi_state.set_on_start_request(
        [](std::uint16_t /*cell_id*/,
           const scf_fapi_body_header_t & /*body_hdr*/,
           std::uint32_t /*body_len*/) -> scf_fapi_error_codes_t {
            // Initialize cell for operation
            return static_cast<scf_fapi_error_codes_t>(SCF_FAPI_MSG_OK);
        });

// Set callback for STOP.request
fapi_state.set_on_stop_request(
        [](std::uint16_t /*cell_id*/,
           const scf_fapi_body_header_t & /*body_hdr*/,
           std::uint32_t /*body_len*/) -> scf_fapi_error_codes_t {
            // Clean up cell resources
            return static_cast<scf_fapi_error_codes_t>(SCF_FAPI_MSG_OK);
        });

Configuration callbacks return error codes to indicate validation results. The state machine automatically sends appropriate responses or error indications based on callback return values.

File Capture and Replay#

FAPI messages can be captured to binary files during execution and replayed later for deterministic testing without NVIPC dependencies. This simplifies unit testing of downstream modules that depend on FAPI messages, such as PHY processing pipelines and ORAN C-plane message preparation.

Capturing Messages#

const auto output_path = get_test_output_dir() / "fapi_capture_sample.fapi";

// Create file writer
ran::fapi::FapiFileWriter writer(output_path.string());

// Create sample message data
ran::fapi::FapiMessageData msg{};
msg.cell_id = 0;
msg.msg_id = SCF_FAPI_DL_TTI_REQUEST;

std::vector<std::uint8_t> msg_buffer(128);
msg.msg_buf = std::span<const std::uint8_t>{msg_buffer.data(), msg_buffer.size()};

// Capture message
writer.capture_message(msg);

// Write to file
writer.flush_to_file();

FapiFileWriter buffers messages in memory during capture and writes them in a single operation to a binary file. The file format includes a header with message count and per-message metadata.

Reading Messages#

// Open FAPI capture file
ran::fapi::FapiFileReader reader(file_path.string());

// Read messages
std::size_t msg_count{};
while (auto msg = reader.read_next()) {
    msg_count++;
}

FapiFileReader provides sequential or bulk access to captured messages:

ran::fapi::FapiFileReader reader(file_path.string());

// Read all messages at once
const auto all_messages = reader.read_all();

Replaying Messages#

FapiFileReplay provides timed replay of UL_TTI_REQUEST messages with automatic SFN/slot field updates:

static constexpr std::uint8_t SLOTS_PER_SUBFRAME = 2;

// Load FAPI capture file for replay
ran::fapi::FapiFileReplay replay(file_path.string(), SLOTS_PER_SUBFRAME);

// Get cell IDs
const auto &cell_ids = replay.get_cell_ids();

// Get request for current slot
for (const auto cell_id : cell_ids) {
    auto req = replay.get_request_for_current_slot(cell_id);
    if (req) {
        // Process request (timing is updated automatically)
    }
}

// Advance to next slot
const auto next_slot = replay.advance_slot();

The replay system maintains per-cell request indices and automatically updates timing fields in FAPI structures to match the current replay position. This enables deterministic testing with real capture data.

Additional Examples#

For complete working examples, see:

  • ran/runtime/fapi/tests/fapi_sample_tests.cpp - Documentation examples and basic usage patterns

  • ran/runtime/fapi/tests/fapi_state_tests.cpp - State machine tests with message processing

  • ran/runtime/fapi/tests/fapi_file_io_tests.cpp - File capture and reader tests

  • ran/runtime/fapi/tests/fapi_file_replay_tests.cpp - Replay system tests with timing validation

API Reference#

enum class ran::fapi::FapiStateT : std::uint8_t#

FAPI state machine states for each cell

Values:

enumerator FapiStateIdle#

Cell not configured.

enumerator FapiStateConfigured#

Cell configured but not running.

enumerator FapiStateRunning#

Cell running and processing slots.

enumerator FapiStateStopped#

Cell stopped after running.

template<typename T>
inline T *ran::fapi::assume_cast(void *ptr)#

Cast a pointer assuming it’s safe

Use this for pointers where the caller has already ensured the pointer points to sufficient memory for type T (e.g., pointers returned from allocation or message construction functions). Centralizes reinterpret_cast with linter suppression.

Performs compile-time and runtime alignment checks:

  • Compile-time: Verifies type alignment is reasonable

  • Runtime: Validates pointer is properly aligned for type T

Note

For packed structs, alignof(T) == 1, so alignment checks verify byte-alignment only. The compiler handles unaligned field access, but this may cause performance penalties on some architectures.

Note

This cast is still technically UB per C++ standard (violates strict aliasing and object lifetime rules), but is widely used in systems/ networking code with proper precautions.

Template Parameters:

T – Type to cast to

Parameters:

ptr[in] Pointer assumed to be safe

Returns:

Pointer cast to type T

template<typename T>
inline const T *ran::fapi::assume_cast(
const void *ptr,
)#

Cast a const pointer assuming it’s safe

Use this for pointers where the caller has already ensured the pointer points to sufficient memory for type T (e.g., pointers from validated messages). Centralizes reinterpret_cast with linter suppression.

Performs compile-time and runtime alignment checks:

  • Compile-time: Verifies type alignment is reasonable

  • Runtime: Validates pointer is properly aligned for type T

Note

For packed structs, alignof(T) == 1, so alignment checks verify byte-alignment only. The compiler handles unaligned field access, but this may cause performance penalties on some architectures.

Note

This cast is still technically UB per C++ standard (violates strict aliasing and object lifetime rules), but is widely used in systems/ networking code with proper precautions.

Template Parameters:

T – Type to cast to

Parameters:

ptr[in] Pointer assumed to be safe

Returns:

Const pointer cast to type T

template<typename T, typename U>
inline T &ran::fapi::assume_cast_ref(
U &ref,
)#

Cast a reference assuming it’s safe

Use this for references where the caller has already ensured the reference is properly typed (e.g., message body headers). Centralizes reinterpret_cast with linter suppression.

Performs compile-time and runtime alignment checks:

  • Compile-time: Verifies type alignment is reasonable

  • Runtime: Validates reference address is properly aligned for type T

Note

For packed structs, alignof(T) == 1, so alignment checks verify byte-alignment only. The compiler handles unaligned field access, but this may cause performance penalties on some architectures.

Note

This cast is still technically UB per C++ standard (violates strict aliasing and object lifetime rules), but is widely used in systems/ networking code with proper precautions.

Template Parameters:
  • T – Type to cast to

  • U – Source type of reference

Parameters:

ref[inout] Reference assumed to be safe

Returns:

Reference cast to type T

template<typename T, typename U>
inline const T &ran::fapi::assume_cast_ref(
const U &ref,
)#

Cast a const reference assuming it’s safe

Use this for references where the caller has already ensured the reference is properly typed (e.g., message body headers). Centralizes reinterpret_cast with linter suppression.

Performs compile-time and runtime alignment checks:

  • Compile-time: Verifies type alignment is reasonable

  • Runtime: Validates reference address is properly aligned for type T

Note

For packed structs, alignof(T) == 1, so alignment checks verify byte-alignment only. The compiler handles unaligned field access, but this may cause performance penalties on some architectures.

Note

This cast is still technically UB per C++ standard (violates strict aliasing and object lifetime rules), but is widely used in systems/ networking code with proper precautions.

Template Parameters:
  • T – Type to cast to

  • U – Source type of reference

Parameters:

ref[in] Reference assumed to be safe

Returns:

Const reference cast to type T

inline std::span<std::byte> ran::fapi::make_buffer_span(
void *ptr,
const std::size_t length,
)#

Create a byte span from a raw pointer and length

Parameters:
  • ptr[in] Raw pointer to data

  • length[in] Length of the data in bytes

Returns:

Byte span over the data

inline std::span<const std::byte> ran::fapi::make_const_buffer_span(
const void *ptr,
const std::size_t length,
)#

Create a const byte span from a raw pointer and length

Parameters:
  • ptr[in] Raw pointer to data

  • length[in] Length of the data in bytes

Returns:

Const byte span over the data

ran::fapi::DECLARE_LOG_COMPONENT(
FapiComponent,
FapiCore,
FapiState,
FapiMsg,
FapiSample,
FapiFileReplay,
)#

Declare logging components for FAPI subsystem

std::string ran::fapi::create_default_nvipc_config(
std::string_view prefix,
)#

Create default NVIPC configuration YAML string

Generates standard NVIPC configuration for PHY-MAC interface with customizable prefix. The configuration includes default buffer sizes, memory pool settings, and application configuration suitable for most FAPI applications.

Parameters:

prefix[in] NVIPC shared memory prefix (e.g., “fapi_sample”, “phy_ran_app”)

Returns:

YAML configuration string ready for FapiState initialization

tl::expected<std::filesystem::path, std::string> ran::fapi::get_fapi_capture_file_path(
)#

Get FAPI capture file path from environment variables

Reads FAPI_CAPTURE_DIR and TEST_CELLS environment variables, validates their values, and constructs the path to the FAPI capture file.

Returns:

Path to FAPI capture file on success, error message on failure

struct CapturedFapiMessage#
#include <fapi_file_writer.hpp>

Captured FAPI message

Used by both FapiFileWriter and FapiFileReader for storing FAPI message data with optional data buffer.

Public Members

uint16_t cell_id = {}#

Cell identifier.

uint16_t msg_id = {}#

FAPI message type.

std::vector<uint8_t> msg_data#

body_hdr + body

std::vector<uint8_t> data_buf#

optional data buffer

struct CellConfig#
#include <fapi_state.hpp>

Per-cell configuration and state

Public Members

std::atomic<FapiStateT> state = {FapiStateT::FapiStateIdle}#

Current FAPI state (atomic for thread-safe access)

uint16_t cell_id = {}#

Cell index.

uint16_t phy_cell_id = {}#

Physical cell ID from CONFIG.request.

uint16_t num_rx_ant = {0}#

Number of RX antennas from CONFIG.request.

struct FapiFileHeader#
#include <fapi_file_writer.hpp>

File format header (16 bytes)

Public Members

std::array<char, 4> magic = {}#

Magic identifier “FAPI”.

uint32_t version = {}#

File format version.

uint32_t message_count = {}#

Total number of messages.

uint32_t reserved = {}#

Reserved for future use.

Public Static Attributes

static constexpr uint32_t CURRENT_VERSION = 1#

File format version.

static constexpr std::array<char, 4> MAGIC_CHARS = {'F', 'A', 'P', 'I'}#

Magic identifier characters.

class FapiFileReader#
#include <fapi_file_reader.hpp>

FAPI message file reader

Reads FAPI messages from binary files created by FapiFileWriter. Provides sequential access to captured messages for replay or analysis.

Usage:

FapiFileReader reader("/tmp/capture.fapi");
std::map<std::uint16_t, std::vector<std::vector<std::uint8_t>>> fapi_messages;
while (auto msg = reader.read_next()) {
    if (msg->msg_data.empty()) {
        continue;
    }
    const std::uint16_t cell_id = msg->cell_id;
    fapi_messages[cell_id].emplace_back(msg->msg_data.begin(), msg->msg_data.end());
}

Thread safety: Not thread-safe. External synchronization required.

Public Functions

explicit FapiFileReader(std::string input_path)#

Open and validate FAPI file

Parameters:

input_path[in] Path to input file

Throws:

std::runtime_error – if file cannot be opened or is invalid

std::optional<CapturedFapiMessage> read_next()#

Read next message from file

Throws:

std::runtime_error – if file is corrupted

Returns:

Message if available, std::nullopt if EOF or error

std::vector<CapturedFapiMessage> read_all()#

Read all remaining messages from file

Reads all messages from current position to end of file. Useful for loading entire capture files into memory.

Throws:

std::runtime_error – if file is corrupted

Returns:

Vector of all remaining messages

void reset()#

Reset reader to beginning of file

Seeks back to first message for re-reading.

std::size_t get_total_message_count() const noexcept#

Get total message count from file header

Returns:

Total number of messages in file

std::size_t get_messages_read() const noexcept#

Get number of messages read so far

Returns:

Count of messages read

bool is_eof() const noexcept#

Check if at end of file

Returns:

true if EOF or all messages read

class FapiFileReplay#
#include <fapi_file_replay.hpp>

FAPI file replay state

Manages replaying captured FAPI UL_TTI_REQUEST messages with proper timing updates. Tracks current slot position and updates SFN/slot fields in FAPI buffers to match current replay position.

Public Functions

explicit FapiFileReplay(
const std::string &fapi_file_path,
std::uint8_t slots_per_subframe,
)#

Load FAPI UL_TTI_REQUEST messages from capture file

Parameters:
  • fapi_file_path[in] Path to .fapi capture file

  • slots_per_subframe[in] Slots per subframe (numerology-dependent)

Throws:

std::runtime_error – if file cannot be loaded or contains no valid messages

std::uint64_t advance_slot()#

Advance to next slot

Increments the absolute slot counter and updates frame/subframe/slot_id state. For each cell, checks if the current request matched the previous slot (before advancing). If a match is found, that request was consumed, so advances that cell’s request index to the next request (with wraparound).

This centralizes the file state update in one place. After calling this method, get_request_for_current_slot() will return requests for the new slot.

This should be called once per slot by the application, not per cell.

Returns:

Current absolute slot number after increment

std::optional<RequestWithSize> get_request_for_current_slot(
std::uint16_t cell_id,
)#

Get request for current slot and cell

Returns the FAPI request buffer for the given cell that matches the current slot timing. Updates the SFN and slot fields in the buffer to match current replay position. Returns std::nullopt if no matching request or cell not found.

This method can be called multiple times for the same slot without side effects on the request index state (which is advanced by advance_slot()).

Note

This method updates the returned FAPI buffer’s timing fields (sfn, slot) to match the current replay position, but does not advance internal request indices.

Parameters:

cell_id[in] Cell identifier

Returns:

Optional RequestWithSize containing pointer and body_len, or std::nullopt if no match

const std::vector<std::uint16_t> &get_cell_ids() const noexcept#

Get all cell IDs present in loaded data

Returns:

Const reference to vector of cell IDs

std::size_t get_request_count(std::uint16_t cell_id) const noexcept#

Get number of requests for a cell

Parameters:

cell_id[in] Cell identifier

Returns:

Number of requests, or 0 if cell not found

std::size_t get_total_request_count() const noexcept#

Get total request count across all cells

Returns:

Total number of requests

inline std::size_t get_cell_count() const noexcept#

Get number of cells

Returns:

Cell count

inline std::uint64_t get_current_absolute_slot() const noexcept#

Get current absolute slot number

Returns:

Current absolute slot

FapiSlotTiming get_current_slot_timing() const noexcept#

Get current slot timing

Returns:

Current frame/subframe/slot timing

struct RequestWithSize#
#include <fapi_file_replay.hpp>

Result of request lookup containing pointer and body length

Public Members

scf_fapi_ul_tti_req_t *request = {}#

Non-owning pointer to request structure (owned by FapiFileReplay)

std::size_t body_len = {}#

Body length (buffer size minus body header)

class FapiFileWriter#
#include <fapi_file_writer.hpp>

FAPI message file writer

Captures FAPI messages to memory during execution and writes them to a binary file on flush. Designed for use with FapiState callbacks to record messages for later replay without NVIPC dependencies.

Usage:

FapiFileWriter writer("/tmp/capture.fapi");
fapi_state.set_on_message([&writer](const FapiMessageData& msg) {
    writer.capture_message(msg);
});
// ... run test ...
writer.flush_to_file();

Thread safety: Not thread-safe. External synchronization required if called from multiple threads.

Public Functions

explicit FapiFileWriter(
std::string output_path,
std::size_t initial_capacity = DEFAULT_INITIAL_CAPACITY,
)#

Construct file writer

Parameters:
  • output_path[in] Path to output file (will be created/overwritten)

  • initial_capacity[in] Initial message capacity to reserve

void capture_message(const FapiMessageData &msg)#

Capture a message to memory buffer

Called from FapiState on_message callback. Copies message data to internal buffer for later writing.

Parameters:

msg[in] Message data to capture

void flush_to_file()#

Write all buffered messages to file

Writes file header followed by all captured message records. Should be called once at end of capture session.

Throws:

std::runtime_error – if file operations fail

std::size_t get_message_count() const noexcept#

Get number of captured messages

Returns:

Count of messages in buffer

std::size_t get_buffer_size_bytes() const noexcept#

Get total buffer size in bytes

Returns:

Total size of all captured data including headers

Public Static Attributes

static constexpr std::size_t DEFAULT_INITIAL_CAPACITY = 1000#

Default message capacity.

struct FapiMessageData#
#include <fapi_state.hpp>

Message data for capture/replay

Contains raw FAPI message data without transport-specific dependencies. Uses std::span for zero-copy view of message buffers.

Public Members

uint16_t cell_id = {}#

Cell identifier.

scf_fapi_message_id_e msg_id = {}#

FAPI message type.

std::span<const uint8_t> msg_buf#

FAPI body (body_hdr + body)

std::span<const uint8_t> data_buf#

Optional data buffer (empty if not present)

struct FapiMessageRecordHeader#
#include <fapi_file_writer.hpp>

Message record header (12 bytes)

Public Members

uint16_t cell_id#

Cell identifier.

uint16_t msg_id#

FAPI message type.

uint32_t msg_len#

Length of message data.

uint32_t data_len#

Length of data buffer (0 if none)

struct FapiSlotTiming#
#include <fapi_file_replay.hpp>

Slot timing information for FAPI replay

Contains raw timing data needed for conversion to ORAN slot timing. This is independent of ORAN to avoid circular dependencies.

Public Members

std::uint64_t absolute_slot = {}#

Absolute slot number since slot 0.

std::uint8_t slots_per_subframe = {}#

Slots per subframe (numerology-dependent)

class FapiState#
#include <fapi_state.hpp>

FAPI state machine manager for 5G NR PHY-MAC interface

Provides comprehensive management of FAPI message processing, including:

  • NVIPC transport lifecycle (initialization, message send/receive, cleanup via RAII)

  • Per-cell state machines (Idle → Configured → Running → Stopped transitions)

  • Message routing and validation for CONFIG/START/STOP.request messages

  • User-configurable callbacks for UL_TTI_REQUEST, DL_TTI_REQUEST, SLOT_RESPONSE forwarding

  • Automatic SLOT.indication generation with SFN/slot tracking and wraparound

  • ERROR.indication and STOP.indication transmission

Message flow:

  1. Receive messages via NVIPC (receive_message)

  2. Process and route via process_message (handles state transitions, invokes callbacks)

  3. Send indications via NVIPC (send_slot_indication, send_stop_indication, etc.)

State machine: Each cell independently transitions through states based on CONFIG.request → START.request → STOP.request message sequence.

Thread safety:

Note

Destructor automatically frees NVIPC resources and unlinks /dev/shm files via RAII through NvIpcDeleter.

Public Types

using OnUlTtiRequestCallback = std::function<void(uint16_t cell_id, const scf_fapi_body_header_t &body_hdr, uint32_t body_len)>#

Callback function type for UL TTI request events

Param cell_id:

[in] Cell identifier

Param body_hdr:

[in] Message body header

Param body_len:

[in] Length of message body

using OnDlTtiRequestCallback = std::function<void(uint16_t cell_id, const scf_fapi_body_header_t &body_hdr, uint32_t body_len)>#

Callback function type for DL TTI request events

Param cell_id:

[in] Cell identifier

Param body_hdr:

[in] Message body header

Param body_len:

[in] Length of message body

using OnSlotResponseCallback = std::function<void(uint16_t cell_id, const scf_fapi_body_header_t &body_hdr, uint32_t body_len)>#

Callback function type for slot response events

Param cell_id:

[in] Cell identifier

Param body_hdr:

[in] Message body header

Param body_len:

[in] Length of message body

using OnConfigRequestCallback = std::function<scf_fapi_error_codes_t(uint16_t cell_id, const scf_fapi_body_header_t &body_hdr, uint32_t body_len)>#

Callback function type for config request events

Called when a CONFIG.request is received. Return error code to indicate success or failure.

Param cell_id:

[in] Cell identifier

Param body_hdr:

[in] Message body header

Param body_len:

[in] Length of message body

Return:

Error code indicating validation result

using OnStartRequestCallback = std::function<scf_fapi_error_codes_t(uint16_t cell_id, const scf_fapi_body_header_t &body_hdr, uint32_t body_len)>#

Callback function type for start request events

Called when a START.request is received. Return error code to indicate success or failure.

Param cell_id:

[in] Cell identifier

Param body_hdr:

[in] Message body header

Param body_len:

[in] Length of message body

Return:

Error code indicating validation result

using OnStopRequestCallback = std::function<scf_fapi_error_codes_t(uint16_t cell_id, const scf_fapi_body_header_t &body_hdr, uint32_t body_len)>#

Callback function type for stop request events

Called when a STOP.request is received. Return error code to indicate success or failure.

Param cell_id:

[in] Cell identifier

Param body_hdr:

[in] Message body header

Param body_len:

[in] Length of message body

Return:

Error code indicating validation result

using OnMessageCallback = std::function<void(const FapiMessageData &msg)>#

Callback function type for capturing all messages

Called for every message before routing to specific handlers. Provides raw message data for recording/replay purposes without NVIPC dependencies.

Param msg:

[in] Message data including buffers and metadata

Public Functions

explicit FapiState(const InitParams &params)#

Construct and initialize FAPI state machine

Initializes NVIPC from config file or string in constructor. If nvipc_config_file is provided, uses file; otherwise uses nvipc_config_string. Throws std::runtime_error if NVIPC initialization fails.

Parameters:

params[in] Configuration parameters

int allocate_message(nv_ipc_msg_t &msg)#

Allocate a TX message buffer with proper synchronization

Allocates a buffer from the NVIPC free pool with acquire fence to ensure the buffer is clean after allocation.

Parameters:

msg[out] Message structure to populate

Returns:

0 on success, negative on failure

int receive_message(nv_ipc_msg_t &msg)#

Receive a message from NVIPC

Non-blocking receive. Returns negative value if no message available. Forwards the return code from NVIPC rx_recv_msg().

Parameters:

msg[out] Message structure to populate

Returns:

>= 0 on success, < 0 if no message available (NVIPC returns -1)

void release_message(nv_ipc_msg_t &msg)#

Release a message buffer back to NVIPC

Must be called after processing each received message.

Parameters:

msg[inout] Message to release

scf_fapi_error_codes_t process_message(nv_ipc_msg_t &msg)#

Process incoming FAPI message (main entry point)

Routes message to appropriate handler based on message type. Handles CONFIG.request, START.request, STOP.request, and callbacks for UL_TTI_REQUEST, DL_TTI_REQUEST, SLOT_RESPONSE.

Parameters:

msg[inout] NVIPC message to process

Returns:

Error code indicating success (MSG_OK) or failure reason

bool send_slot_indication()#

Send SLOT.indication to all running cells (thread-safe)

Atomically captures current slot at start to ensure consistency across all cells in a single invocation.

Returns:

true if all messages sent successfully

bool send_stop_indication(uint16_t cell_id)#

Send STOP.indication message for a cell

Parameters:

cell_id[in] Cell identifier

Returns:

true if message sent successfully

bool send_error_indication(
uint16_t cell_id,
scf_fapi_message_id_e msg_id,
scf_fapi_error_codes_t error_code,
)#

Send ERROR.indication message (thread-safe)

Atomically captures current slot to include in error indication.

Parameters:
  • cell_id[in] Cell identifier

  • msg_id[in] Message ID that caused error

  • error_code[in] Error code to report

Returns:

true if message sent successfully

void increment_slot()#

Increment slot counter with wraparound (thread-safe)

Advances to next slot, wrapping SFN and slot as needed. Uses lock-free compare-and-swap to handle concurrent access.

void reset_slot()#

Reset slot counter to (0, 0) (thread-safe)

Atomically resets both SFN and slot to zero.

SlotInfo get_current_slot() const noexcept#

Get current slot information (thread-safe)

Atomically reads current SFN and slot without tearing.

Returns:

Current SFN and slot

FapiStateT get_cell_state(uint16_t cell_id) const noexcept#

Get state of a specific cell

Parameters:

cell_id[in] Cell identifier

Returns:

Cell state, or idle if invalid cell_id

std::size_t get_num_cells_configured() const noexcept#

Get number of configured cells

Returns:

Count of cells in configured or later states

std::size_t get_num_cells_running() const noexcept#

Get number of running cells

Returns:

Count of cells in running state

void set_on_ul_tti_request(OnUlTtiRequestCallback callback)#

Set callback for UL TTI request events

Parameters:

callback[in] Callback function to invoke on UL TTI request

void set_on_dl_tti_request(OnDlTtiRequestCallback callback)#

Set callback for DL TTI request events

Parameters:

callback[in] Callback function to invoke on DL TTI request

void set_on_slot_response(OnSlotResponseCallback callback)#

Set callback for slot response events

Parameters:

callback[in] Callback function to invoke on slot response

void set_on_config_request(OnConfigRequestCallback callback)#

Set callback for config request events

Parameters:

callback[in] Callback function to invoke on CONFIG.request

void set_on_start_request(OnStartRequestCallback callback)#

Set callback for start request events

Parameters:

callback[in] Callback function to invoke on START.request

void set_on_stop_request(OnStopRequestCallback callback)#

Set callback for stop request events

Parameters:

callback[in] Callback function to invoke on STOP.request

void set_on_message(OnMessageCallback callback)#

Set callback for capturing all messages

This callback is invoked before message-specific callbacks, allowing capture of all messages for recording/replay purposes.

Parameters:

callback[in] Callback function to invoke for every message

struct InitParams#
#include <fapi_state.hpp>

Configuration parameters for FapiState

Public Members

std::string nvipc_config_file#

Full path to NVIPC config YAML.

std::string nvipc_config_string#

NVIPC config YAML as string (used if nvipc_config_file is empty)

std::size_t max_cells = {DEFAULT_MAX_CELLS}#

Maximum supported cells.

uint16_t max_sfn = {DEFAULT_MAX_SFN}#

Maximum SFN value (wraps to 0)

uint16_t max_slot = {DEFAULT_MAX_SLOT}#

Maximum slot value (wraps to 0)

Public Static Attributes

static constexpr std::size_t DEFAULT_MAX_CELLS = 20#

Default maximum cells.

static constexpr uint16_t DEFAULT_MAX_SFN = 1024#

Default maximum SFN.

static constexpr uint16_t DEFAULT_MAX_SLOT = 20#

Default maximum slot.

struct SlotInfo#
#include <fapi_state.hpp>

5G NR slot timing information

Public Functions

auto operator<=>(const SlotInfo&) const = default#

Three-way comparison operator

Returns:

std::strong_ordering result of comparison

Public Members

uint16_t sfn = {}#

System Frame Number (0-1023)

uint16_t slot = {}#

Slot number (0-19 for 30kHz SCS)

class YamlConfigBuilder#
#include <fapi_test_utils.hpp>

Helper class for building NVIPC YAML configurations for tests

Public Static Functions

static std::string create_primary_config(
const std::string &prefix = "fapi_test",
)#

Create primary NVIPC configuration

Parameters:

prefix[in] NVIPC prefix for shared memory

Returns:

YAML configuration string

static std::string create_secondary_config(
const std::string &prefix = "fapi_test",
)#

Create secondary NVIPC configuration

Parameters:

prefix[in] NVIPC prefix for shared memory

Returns:

YAML configuration string