From the core to the antenna: a radio-side primer
You know AMF, SMF and UPF. This walks the other way, from a phone's first transmission through the radio unit and the gNB, and explains split 7.2 versus split 8, the four fronthaul planes, and what a radio driver actually does.

1. The stack, from the antenna to the core
A 5G gNB is one logical node, but it is usually built from three physical pieces. Reading from the phone toward the core, the five boxes below are what a packet passes through. The core-side reader already owns the box on the right; this article is about the two on the left and the link between them.
Five nodes in a row, UE, RU, DU, CU and 5GC, over a rail. The hop between RU and DU is the fronthaul, drawn in copper; Uu, F1 and NG are hairlines. A green packet rides the rail from the UE to the core, resting under each box while the box lights; it slows over the copper fronthaul hop to mark it as the strict link, and the words hard deadlines appear there. A small white reply returns to the UE.
- a packet, UE to core
- the fronthaul hop: the signal itself, hard deadlines
- a protocol interface over IP
UE
- Phone or modem, or a software UE such as OAI UE in a lab
- Full stack: PHY, MAC, RLC, PDCP, RRC, NAS
- Talks over the air (Uu)
RU: radio unit
- Antenna, power amplifier, up/down conversion, ADC/DAC
- In split 7.2 also the Low-PHY: FFT/IFFT, cyclic prefix, PRACH filtering, beamforming
- In split 8: radio only, no baseband processing
- A custom SDR platform used this way is an RU
DU: distributed unit
- High-PHY: channel coding, modulation, PRACH detection, channel estimation
- MAC: scheduler, HARQ, random access, timing advance commands
- RLC
- In split 8 also the Low-PHY
- In an open gNB such as OCUDU, this is most of the code
CU: central unit
- CU-CP: RRC, PDCP-C
- CU-UP: PDCP-U, SDAP (the E1 interface joins the two)
- Terminates NG toward the AMF and the UPF
- Also part of OCUDU
5GC
- AMF, SMF, UPF, AUSF, UDM. Open5GS or free5GC in a lab, a commercial 5G core in production
- NAS ends here
- Everything you already know
The one-line translation. F1 and NG are to the RAN what SBI and the N-interfaces are to the core: protocol interfaces between logical nodes, carried over IP. The fronthaul between RU and DU is different in kind. It carries the signal itself, sampled, with hard real-time deadlines. Nothing in the core looks like it.
2. Split 7.2 versus split 8: where the FFT lives
The "split" number says where the PHY chain is cut between RU and DU. Two cuts matter in practice, and they lead to two different kinds of radio and two different kinds of fronthaul.
Split 7.2 (O-RAN fronthaul). The RU does the Low-PHY. On receive it runs the FFT and removes the cyclic prefix, so what crosses to the DU is frequency-domain IQ: one packet per antenna per symbol, and only the resource blocks that carry something.
The link is eCPRI over Ethernet with four planes: U (samples), C (per-slot scheduling information for the RU), S (PTP timing) and M (management). The link rate is modest and the RU is smart. Every commercial O-RU works this way.
Split 8. The RU is a pure radio: ADC, DAC and RF. What crosses to the DU is the raw time-domain sample stream, every sample, continuously, each block stamped with the exact time it was (or must be) at the antenna. The DU does the FFT and everything above it.
The link rate is high (122.88 MSps × 4 bytes × antennas) and the RU is simple. This is how a USRP works with the UHD driver, and it is how any custom SDR platform talks to a DU when there is no Low-PHY in the radio.
Two columns side by side, split 7.2 and split 8, each the same chain of five blocks from top to bottom: High-PHY, beamforming, IFFT or FFT, cyclic prefix, and DAC, ADC and RF. A dashed line marks the cut between DU and radio: in split 7.2 it sits under the High-PHY, in split 8 under the cyclic prefix. A block of samples travels down both columns at the same time and each block lights as it passes; the cut lights with the word fronthaul as the samples cross it, early in split 7.2 and late in split 8.
- a block of samples, DU toward the antenna
- the cut: the fronthaul starts here
Why anyone chooses split 8
Split 7.2 is the right answer for an operator buying radios: the standard fixes what crosses the wire, so any O-RU should work with any O-DU. Split 8 is the right answer when the radio is a software-defined platform and the people using it want to change the signal chain itself. Beam handling, satellite delay and Doppler models, or a non-standard waveform can all be edited on the server when every stage of the PHY runs in software. With split 7.2 the FFT and the beamforming are fixed inside the radio, which is exactly what an operator wants and exactly what a research platform does not.
3. USRP, UHD, ZMQ, Aurora, eCPRI: what each one is and where it sits
These names come up in every radio integration, and they are not the same kind of thing. Some are hardware, some are drivers, some are wire formats, and one is a link protocol inside a box. They line up as layers between the DU and the antenna.
USRP
A family of software-defined radios from NI/Ettus (the B210 over USB 3, the X310 and N310 over 10 GbE). Hardware: RF, ADC/DAC and a small FPGA. In split-8 terms it is the RU.
UHD
The USRP Hardware Driver: a C++ library on the host that talks to USRPs. It exposes "send these samples at this time", "receive samples" and "set frequency, gain and rate". On the wire it uses its own packet format (CHDR, VITA-49-like) with a timestamp per packet. OCUDU has a uhd radio driver that calls it.
ZMQ radio driver
OCUDU's software-only radio. Instead of a radio it pushes the sample stream through a ZeroMQ socket to a peer that does the same (another gNB or a UE stack). No hardware, no PPS, no real timing: the two stacks pace each other.
Custom SDR platform + your driver
A custom SDR platform has no UHD. So you write a driver for the same driver slot, and below it sits the vendor's transport instead of CHDR.
Aurora frames
Xilinx's lightweight point-to-point protocol for high-speed serial links between FPGAs. Inside an SDR platform the radio front end may stream header-plus-data Aurora frames to a bridge FPGA that owns the Ethernet port. Nobody outside the box sees Aurora.
The vendor's packet format
The Ethernet packet the platform sends to the server's NIC. Typically a custom EtherType, a fixed header (packet counter, slot counter, burst flags, sync word, a reserved word or two), then a fixed-size block of IQ samples. The receive direction is usually documented first; the transmit direction often has to be agreed.
DPDK rings and mbufs
Inside the server, the vendor's packet engine owns the NIC and a pinned memory pool. It strips the Ethernet header and hands each packet's payload to consumers through lock-free queues. Your driver attaches to those queues as a DPDK secondary process.
eCPRI
The O-RAN fronthaul transport for split 7.2: Ethernet (EtherType 0xAEFE) or UDP, carrying frequency-domain IQ (U-plane) and per-slot control (C-plane) messages with a common header. Used by every commercial O-RU and by OCUDU's ofh layer.
The same layers, four ways
| Layer | USRP + UHD (split 8 reference) | ZMQ (software-only lab) | Custom SDR platform (split 8, your driver) | O-RAN split 7.2 (commercial O-RU) |
|---|---|---|---|---|
| gNB software | OCUDU DU with the Low-PHY (FFT) inside | OCUDU DU with the Low-PHY inside | OCUDU DU with the Low-PHY inside, unchanged | OCUDU O-DU, High-PHY only |
| Radio driver slot | uhd driver to libuhd | zmq driver | your driver | ofh (Open Fronthaul) module, not a radio driver |
| Host transport | libuhd to USB 3 or NIC (optionally DPDK inside UHD) | ZeroMQ TCP socket | DPDK rings in hugepages to the vendor's packet engine to NIC | DPDK or kernel Ethernet in OCUDU's ofh |
| Wire format | CHDR packets (timestamp, sequence, sc16 samples) | none, raw sample chunks | vendor packet: header + fixed-size IQ block | eCPRI U-plane (IQ) and C-plane (control) messages |
| Link framing below that | n/a | n/a | often an FPGA-to-FPGA link (Aurora) between front end and bridge | n/a (Ethernet PHY) |
| Radio | USRP: RF + ADC/DAC + small FPGA | none | SDR front end: RF + ADC/DAC, several TX and RX chains, PPS/GPS in | O-RU: RF + ADC/DAC + Low-PHY + beamforming |
| What crosses to the radio | time-domain samples + timestamp | time-domain samples | time-domain samples + timestamp (in the vendor header) | frequency-domain IQ per symbol + control |
| RF configuration | UHD API calls at start (freq, gain, rate) | n/a | gRPC or vendor API at start (freq, gain, rate): the M-Plane role | M-Plane: NETCONF/YANG (Open M-Plane) |
| Timing source | USRP clock, PPS/GPSDO optional | none (stacks pace each other) | PPS/GPS at the radio to timestamp field in the header | S-plane: PTP / SyncE from a grandmaster |
Reading the table left to right: the custom-SDR column is the USRP column with two rows swapped out, the driver (yours) and the transport and wire format (the vendor's). Everything above the driver slot is the same DU binary. That is the whole engineering claim behind a split-8 driver, and it is what keeps the job contained.
4. The four planes, and where Open M-Plane fits
The four planes are an O-RAN split 7.2 concept. They partition everything that crosses the fronthaul between an O-RU and an O-DU by purpose, and each has its own protocol. The useful thing for a split-8 reader is that every plane has an analogue, even when the analogue is a single field in a packet header.
An O-DU on the left and an O-RU on the right, joined by a fronthaul band with four lanes, each with a tag: U for user, C for control, S for synchronisation and M for management. One lane at a time, a short packet crosses from the O-DU to the O-RU while that lane's tag and name light: a data packet on U, a quick control packet on C, a small dot that advances in regular ticks on S, and a slow dot on M. Then a pause, and the tour repeats.
- U-plane: the samples
- C-plane: per-slot control
- S-plane: the clock, a regular tick
- M-Plane: configuration, slow
U-plane: user
the signal itselfFrequency-domain IQ samples per antenna per symbol, only the allocated resource blocks. Carried in eCPRI (message type 0).
transmit() and receive() calls, one slot of time-domain samples per antenna.C-plane: control
real time, per slotTells the O-RU what the next slot looks like: which resource blocks, which beams, PRACH occasions, mixed numerology. Carried in eCPRI (message type 2). Without it the O-RU cannot interpret the U-plane.
S-plane: synchronisation
the clockPTP (IEEE 1588) and SyncE from a grandmaster so O-RU and O-DU share time and frequency to sub-microsecond accuracy. Slot boundaries are derived from it.
M-Plane: management
configuration, not real timeNETCONF over SSH or TLS with YANG data models. The O-DU (or an EMS) configures the O-RU: carriers, frequencies, gains, delays, software upgrade, alarms, performance counters. Defined in the O-RAN WG4 M-Plane specification, separate from the CUS-plane specification.
set_rate, set_freq and set_gain, or a vendor gRPC service.What Open M-Plane is
In a split-7.2 deployment, Open M-Plane is the piece that implements the O-RAN M-Plane: a NETCONF/YANG management path between the O-RU and the O-DU, so the DU can configure the radio and receive its alarms using the standard YANG models rather than a vendor tool. It exists because in split 7.2 the radio is a smart, standardised box that must be managed the standard way.
Open M-Plane, and the four planes as protocols, are 7.2 concepts. Split 8 has no O-RAN specification for the fronthaul at all. A USRP-style radio has no NETCONF agent, no eCPRI and no PTP requirement. What survives into split 8 is the classification: every parameter that crosses to a radio is still either data, real-time control, timing, or configuration. When we write a parameter inventory for a split-8 driver we still bucket every entry as C, U, S or M, because the bucket tells you the periodicity and the mechanism that parameter needs. Open M-Plane itself is not used with a split-8 radio. gRPC or a vendor API plays that role, and the planes are a vocabulary, not a protocol.
Two splits, two vocabularies. Split 7.2: eCPRI fronthaul, an O-RU with the Low-PHY inside, Open M-Plane for management, OCUDU's ofh module. Split 8: raw samples, a plain radio, a radio driver inside the DU, gRPC or a vendor API for configuration. The DU above the cut is the same; the bottom layer is different. Do not let vocabulary from one leak into the other.
5. Architecture side by side
Read each column top to bottom, core to antenna. The tinted bar is the fronthaul, the only tier that differs in kind. The plane tags show where each plane's job is done.
Split 7.2: O-RAN fronthaul
5GC
- AMF · SMF · UPF (a commercial 5G core, or Open5GS in a lab)
CU (OCUDU)
- RRC · PDCP · SDAP · E1 between CU-CP and CU-UP
O-DU (OCUDU)
- MAC scheduler · RLC · High-PHY (coding, modulation, channel estimation, PRACH detection)
- OCUDU
ofhmodule: builds eCPRI C-plane and U-plane per slot; PTP client; NETCONF client via Open M-Plane
CeCPRI type 2: per-slot section descriptions, beam IDs, PRACH configuration
SPTP / SyncE from a grandmaster clock
MNETCONF / YANG via Open M-Plane
O-RU (commercial split-7.2 radio)
- Low-PHY: IFFT/FFT, cyclic prefix, PRACH filtering, digital beamforming
- RF, PA, ADC/DAC, antennas
- NETCONF agent, PTP slave
UE
- Commercial phone or a software UE
Split 8: raw samples
5GC
- Open5GS or free5GC in a lab; a commercial core in production
CU (OCUDU)
- RRC · PDCP · SDAP, identical
DU (OCUDU), now including the Low-PHY
- MAC scheduler · RLC · High-PHY, identical
- Lower PHY: IFFT/FFT, cyclic prefix, PRACH detection, CFO (Doppler) compensation, timing offset
- Radio driver slot: your driver. Re-layout of buffers, vendor header, slot to packets, DPDK secondary attach, gRPC client
Cheader words (packet counter, slot counter, start/end-of-burst) plus a control word agreed with the vendor
SPPS/GPS at the radio to timestamp field in the header to sample ticks in the DU
MgRPC or vendor API: frequency, gain, sample rate, clock source
Custom SDR platform
- Vendor packet engine (DPDK primary): owns the NIC, the rings, packetise and depacketise
- FPGA bridge: serial link on one side, Ethernet on the other
- SDR front end: RF and ADC/DAC only, several TX and RX chains
UE
- Software UE on a USRP via UHD, or a phone
Same core, same CU, same F1, same MAC and High-PHY. The split moves one box (the Low-PHY) from the radio into the DU and replaces a standardised four-protocol fronthaul with a sample stream plus whatever header, timing and configuration path the two sides agree on. Agreeing on that header, timing and configuration path is most of the work when you integrate a custom radio.
6. A UE attaches: every message from PRACH to a PDU session
Step through it. The stage shows five lanes. Each step draws the messages of that step, and the panel explains it, with a note on what differs between split 7.2 and split 8 and a line for the core-side reader. Switch the toggle to see the same sequence in the other split: the lane chips under RU and DU move, and the fronthaul hops change. The arrow keys work once a button has focus.
Cell broadcast: SSB and SIB1
Before anything else, the cell has to be findable. The DU generates the synchronisation signal block every 20 ms and the system information; the RU puts it on the air. The UE searches, locks its clock to the cell, reads MIB and SIB1, and learns where and how to do random access.
An interactive message sequence chart. Five lanes, UE, RU, DU, CU and 5GC, each headed by a node with its functions. Ten steps from cell broadcast to steady-state user plane; each step draws its messages as arrows between lanes, coloured by hop kind, one after another with a green comet riding each arrow and the receiving lane lighting, and a panel beside the chart explains the step, what differs between split 7.2 and split 8, and what the core sees. Buttons step forward and back, play the sequence, and switch the split.
- this step, in order
- Uu, over the air
- fronthaul, RU to DU
- F1, DU to CU
- NG, CU to core
- internal to one lane
Everything from Msg3 upward is identical in both splits and identical to a terrestrial cell. The split only changes the hop between RU and DU. That is why a radio integration is contained: get the fronthaul hop right and the rest of the DU, the CU and the core run unchanged.
7. What a radio driver actually has to do
An open gNB such as OCUDU has a plug-in point for radios. The UHD driver plugs in there for USRPs, and the ZMQ driver plugs in there for software-only tests. When we integrate a custom radio we write a third driver for the same point. The DU does not know or care which one is loaded. This section describes what that third driver has to do, in general terms, and the questions you have to settle with the radio vendor before you can write it.
The driver slot
The DU talks to its radio through a small interface: a radio session with a transmit call that takes one buffer per antenna and the time the first sample must leave the antenna, and a receive call that fills one buffer per antenna and returns the time the first sample arrived. Buffers hold one slot of int16 IQ samples per antenna. The driver's job is to move those buffers into and out of whatever transport sits below it, and to keep the timestamps honest.
UHD is the reference model. Whatever UHD does with CHDR packets over USB or 10 GbE, your driver does with the vendor's packet format over the vendor's transport. Reading the UHD driver in the open gNB before you start is the fastest way to learn the contract you are implementing.
Software-only lab (ZMQ). The DU's built-in ZMQ driver hands samples over a socket straight to a software UE. No radio, no timing discipline, no vendor packet format. Good for proving that the stacks talk to each other; it never touches a custom driver.
Custom SDR through your driver. The same DU and the same interface, but your driver plugs in. It converts the DU's per-antenna slot buffers into the vendor's fixed-size packets with their header, pushes them into a shared-memory ring, and the vendor's packet engine puts them on the wire to the radio. Receive is the mirror image. Frequency, gain and sample rate go to the radio once at start over gRPC or the vendor's API.
Two data paths compared, played in turn. The software-only lab path runs from the DU through the radio session interface and the built-in zmq radio driver over a ZeroMQ socket to a software UE, with no radio. The custom SDR path runs from the same DU and interface through your radio driver into DPDK rings, a vendor packet engine, a fast NIC to FPGA bridge, the SDR front end and an RF cable or antenna to the UE. On each path a slot of samples travels from the DU box to box and each box lights as it passes. Buttons choose a path and send a slot down it again.
- a slot of samples, DU to air
- the driver slot
- the hop between two boxes
What crosses the boundary
On transmit. Take one slot of samples per antenna from the DU. Cut it into fixed-size packets. Put the vendor's header on each one: a packet counter, a slot counter, start-of-burst and end-of-burst flags, and a timestamp derived from the slot start. Enqueue the packets on the shared-memory ring for that antenna. The vendor's packet engine takes them from there.
On receive. Dequeue packets, check the counters for gaps, strip the header, and rebuild the per-antenna buffer. Hand it to the DU together with a sample-count timestamp that says when its first sample was at the antenna. The DU uses that number to find slot boundaries and RACH occasions; if it drifts, PRACH detection stops working and nothing beyond initial access is reliable.
At start. Tell the radio its centre frequency, its gains, its sample rate and its clock source. In split 7.2 this is the M-Plane over NETCONF. With a custom SDR it is usually a gRPC service or the vendor's API, called once when the driver opens the session.
// transmit: called by the DU once per slot, with one buffer per antenna
for each antenna a:
for each fixed-size chunk c of slot_buffer[a]:
hdr.packet_counter = next_packet_counter()
hdr.slot_counter = slot_index
hdr.start_of_burst = (c is the first chunk of the slot)
hdr.end_of_burst = (c is the last chunk of the slot)
hdr.timestamp = slot_start_ticks + offset_in_samples(c)
ring_enqueue(tx_ring[a], hdr + c)
// receive: runs continuously, hands the DU one slot at a time
while running:
pkt = ring_dequeue(rx_ring[a])
if pkt.packet_counter != expected: log the gap, zero-fill the hole
copy pkt.samples into rx_buffer[a] at (pkt.timestamp minus slot_start_ticks)
when rx_buffer[a] is full for every antenna:
deliver(rx_buffer, slot_start_ticks) to the DU
// start-up, once: the M-Plane job
radio.set_frequency(centre_hz); radio.set_gain(tx_db, rx_db)
radio.set_sample_rate(122.88e6); radio.set_clock_source("pps")Four questions to settle before you write a line
Each of these has a habit of arriving half-answered. A vendor document usually describes what the radio sends, because that is what the vendor tested. The rest has to be agreed, and the driver cannot be finished until it is.
The transmit-direction format
Is the server-to-radio packet the mirror of the radio-to-server packet, or does it differ? Does the radio need the start-of-burst and end-of-burst flags to gate its DAC? Does it transmit at the timestamp in the header, or as soon as the packet arrives? If the answer is "as soon as it arrives", the driver has to pace the packets itself.
Who converts the sample rate
The ADC and DAC in an SDR platform often run at a multiple of the 3GPP rate the DU expects (122.88 MSps for a 100 MHz cell). Decimation and interpolation belong in the FPGA. Doing it on the server at these rates costs more CPU than the rest of the DU. Settle which side owns the conversion and what rate is on the wire.
Timestamp and slot-counter semantics
What is tick zero, and which PPS edge sets it? Does the timestamp count samples or packets, at which rate, and when does it wrap? Is the slot counter derived from the timestamp or kept separately? The DU needs one monotonic sample-count clock. Every ambiguity here becomes a PRACH that is never detected.
Which boxes sit in the data path
Is there a bridge FPGA between the front end and the NIC, and does it re-frame the packets? Does the vendor's packet engine strip the Ethernet header before your driver sees the payload? The answer changes which bytes your driver parses and where the latency budget goes.
That is the whole job, and every field in it must be right. The way to get there is a parameter inventory: every value the DU passes to its radio interface, with its width, its periodicity and its mechanism, bucketed as C, U, S or M, and each one mapped to a header field, an API call or a start-up parameter on the radio side. Once the inventory is agreed, writing the driver is the short part.
Glossary for the core-side reader
- PRACH / preamble / Msg1
- The UE's first transmission: a known sequence on a reserved time-frequency slot. The gNB does not know the UE exists before this.
- RAR / Msg2
- Random access response: the gNB answers with a timing advance, a temporary identity and an uplink grant.
- Timing advance (TA)
- How early the UE must transmit so its signal arrives aligned at the gNB. Microseconds on the ground; tens of milliseconds via satellite, which is why NTN adds a common TA and a K-offset.
- SSB / SIB1
- What the cell broadcasts so a UE can find it and read its configuration. The over-the-air equivalent of a service discovering the NRF.
- IQ samples
- The digitised radio signal: pairs of 16-bit integers (in-phase, quadrature) at 122.88 million per second for a 100 MHz cell. This is what crosses the split-8 fronthaul.
- FFT / IFFT
- The transform between time-domain samples and the OFDM subcarriers the standard is written in. Its location defines the split.
- eCPRI
- The O-RAN fronthaul protocol for split 7.2. Not used in split 8.
- UHD / USRP
- NI/Ettus software-defined radios and their driver. OCUDU's reference radio path, and the reference model for any custom split-8 driver.
- DPDK, hugepages, rings, mbufs
- A user-space networking toolkit. The vendor's software owns a large pinned memory area (hugepages) with lock-free queues (rings) of packet buffers (mbufs); your driver attaches to it as a secondary process and reads and writes those queues.
- Shim / plugin / adapter / driver
- Four names for the same thing: the radio driver you write for a custom SDR platform.
- Doppler, CFO
- A moving satellite shifts the carrier frequency; carrier frequency offset is the compensation applied to the samples. Open gNBs with NTN support do this in software, driven by ephemeris data broadcast in SIB19.
- CHDR
- UHD's packet format between host and USRP: a header with sequence number and timestamp, then sc16 samples. The nearest published analogue to a vendor's RF header.
- Aurora
- An FPGA-to-FPGA link protocol you may meet inside an SDR platform. Below Ethernet, not visible to the DU.
- C/U/S/M planes
- The O-RAN 7.2 fronthaul partition: control, user, synchronisation, management. Carried by eCPRI, eCPRI, PTP and NETCONF respectively. Used as a vocabulary in split 8.
- Open M-Plane / NETCONF / YANG
- The O-RAN management path for an O-RU, implemented as a NETCONF/YANG client on the DU side and an agent in the radio. Not used with a split-8 radio; gRPC or a vendor API plays that role.
- PTP / PPS
- Two ways to give a radio a clock: PTP over the network (split 7.2, S-plane) or a one-pulse-per-second signal from GPS into the radio (split 8, USRP or a custom SDR).
- Split 8 vs 7.2
- Where the PHY is cut between RU and DU. In split 8 the RU is a plain radio and raw samples cross; in split 7.2 the RU does the FFT and frequency-domain IQ crosses.
Written by telecogeco, a ugeco brand. The stack, the splits and the attach sequence are as the 3GPP and O-RAN specifications define them; the driver section describes the shape of a typical custom-radio integration and names no specific vendor.