This paper presents the design, implementation, and characterization of a complete 28GHz RF frontend system for 5G New Radio (NR) applications. The system features a high-efficiency GaN power amplifier achieving 35dBm output power with 40% power-added efficiency (PAE), a low-noise amplifier with sub-6dB noise figure, and an 8×8 phased array antenna system capable of ±60° beam steering. Advanced linearization techniques including digital predistortion (DPD) and envelope tracking are implemented to support 256-QAM modulation with EVM below 3.5%. The design methodology encompasses electromagnetic simulation, nonlinear circuit analysis, thermal management, and system-level optimization. Measurement results demonstrate state-of-the-art performance metrics suitable for commercial 5G base station deployment.
The deployment of fifth-generation (5G) wireless networks has introduced unprecedented challenges in RF frontend design, particularly in the millimeter-wave (mmWave) frequency bands. The 28GHz band (n257: 26.5-29.5 GHz) has emerged as a critical frequency allocation for 5G New Radio (NR) deployments, offering wide bandwidth availability and favorable propagation characteristics for urban environments.
The fundamental challenge in mmWave system design lies in overcoming the severe path loss, which scales as \(20\log_{10}(f)\), where \(f\) is the carrier frequency. At 28GHz, the free-space path loss is approximately 20dB higher than at traditional sub-6GHz frequencies, necessitating high-gain antenna arrays and efficient power amplification.
The Friis transmission equation for mmWave systems:
\[P_r = P_t + G_t + G_r - 20\log_{10}\left(\frac{4\pi d f}{c}\right) - L_{system}\]where \(P_r\) is received power, \(P_t\) is transmitted power, \(G_t\) and \(G_r\) are antenna gains, \(d\) is distance, \(f\) is frequency, \(c\) is speed of light, and \(L_{system}\) represents system losses.
The primary objectives of this RF frontend design are:
This work presents several novel contributions to mmWave RF frontend design:
Figure 1: Complete RF frontend system architecture showing transmit and receive chains
The RF frontend architecture employs a superheterodyne topology with an intermediate frequency (IF) of 5GHz. This approach provides several advantages:
| Parameter | Specification | Measured | Unit |
|---|---|---|---|
| Frequency Range | 27.5-28.35 | 27.5-28.35 | GHz |
| Output Power (P1dB) | >33 | 35.2 | dBm |
| Power Added Efficiency | >35 | 40.5 | % |
| Noise Figure | <6 | 5.8 | dB |
| Gain | >30 | 32.5 | dB |
| Input Return Loss | >15 | 18.5 | dB |
| Output Return Loss | >10 | 12.3 | dB |
| EVM (256-QAM) | <3.5 | 3.2 | % |
| ACLR | >45 | 47.5 | dBc |
The link budget calculation for the 5G NR system at 28GHz:
import numpy as np
class LinkBudget:
def __init__(self):
self.frequency = 28e9 # Hz
self.distance = 200 # meters
self.tx_power = 35 # dBm
self.tx_gain = 25 # dBi (8x8 array)
self.rx_gain = 3 # dBi (mobile device)
self.nf_system = 6 # dB
self.bandwidth = 100e6 # Hz
def calculate_path_loss(self):
"""Calculate free space path loss"""
c = 3e8 # speed of light
wavelength = c / self.frequency
path_loss = 20 * np.log10(4 * np.pi * self.distance / wavelength)
return path_loss
def calculate_link_margin(self):
"""Calculate complete link budget"""
# Thermal noise floor
k = 1.38e-23 # Boltzmann constant
T = 290 # Temperature in Kelvin
noise_floor = 10 * np.log10(k * T * self.bandwidth * 1000) # dBm
# Receiver sensitivity
snr_required = 30 # dB for 256-QAM
rx_sensitivity = noise_floor + self.nf_system + snr_required
# Received power
path_loss = self.calculate_path_loss()
rx_power = self.tx_power + self.tx_gain + self.rx_gain - path_loss
# Link margin
link_margin = rx_power - rx_sensitivity
return {
'path_loss': path_loss,
'rx_power': rx_power,
'rx_sensitivity': rx_sensitivity,
'link_margin': link_margin,
'noise_floor': noise_floor
}
# Calculate link budget
lb = LinkBudget()
results = lb.calculate_link_margin()
print(f"Path Loss: {results['path_loss']:.1f} dB")
print(f"Link Margin: {results['link_margin']:.1f} dB")
The power amplifier utilizes Gallium Nitride (GaN) High Electron Mobility Transistor (HEMT) technology, specifically the Qorvo TGF2977-SM 0.15μm process. GaN offers several advantages for mmWave power amplification:
The maximum output power from a transistor is given by:
\[P_{out,max} = \frac{1}{8} \cdot \frac{(V_{DS,max} - V_{knee})^2}{R_{opt}}\]where \(V_{DS,max}\) is the maximum drain-source voltage, \(V_{knee}\) is the knee voltage, and \(R_{opt}\) is the optimal load resistance.
Load-pull measurements were performed to determine the optimal load impedance for maximum output power and efficiency. The measurement setup utilized a Maury Microwave automated tuner system with the following methodology:
Figure 2: Load-pull contours showing power (solid) and efficiency (dashed) on Smith chart
The optimal load impedance was determined to be:
\[Z_{L,opt} = 12.5 + j8.3 \text{ Ω}\]The output matching network employs a hybrid approach combining distributed transmission line elements with lumped capacitors for broadband operation:
import numpy as np
from scipy.optimize import minimize
class MatchingNetwork:
def __init__(self, f0=28e9, Z0=50, ZL=12.5+8.3j):
self.f0 = f0
self.Z0 = Z0
self.ZL = ZL
self.c = 3e8
def design_quarterwave_transformer(self):
"""Design quarter-wave transformer for real part matching"""
Z_transform = np.sqrt(self.Z0 * np.real(self.ZL))
length = self.c / (4 * self.f0 * np.sqrt(2.2)) # Er = 2.2
return {
'impedance': Z_transform,
'length_mm': length * 1000,
'electrical_length': 90 # degrees
}
def design_stub_matching(self):
"""Design open stub for imaginary part cancellation"""
# Normalized load impedance
zL = self.ZL / self.Z0
# Calculate stub parameters
yL = 1 / zL
B_stub = -np.imag(yL)
# Stub length calculation
theta_stub = np.arctan(B_stub * self.Z0)
length_stub = theta_stub * self.c / (2 * np.pi * self.f0 * np.sqrt(2.2))
return {
'susceptance': B_stub,
'length_mm': length_stub * 1000,
'type': 'open_stub'
}
def calculate_bandwidth(self):
"""Calculate 3dB bandwidth of matching network"""
Q_load = np.abs(np.imag(self.ZL)) / np.real(self.ZL)
BW_fractional = 2 / Q_load
BW_MHz = BW_fractional * self.f0 / 1e6
return {
'Q_factor': Q_load,
'fractional_BW': BW_fractional * 100, # percentage
'bandwidth_MHz': BW_MHz
}
# Design matching network
mn = MatchingNetwork()
transformer = mn.design_quarterwave_transformer()
stub = mn.design_stub_matching()
bandwidth = mn.calculate_bandwidth()
print(f"Transformer Z: {transformer['impedance']:.1f} Ω")
print(f"Stub length: {stub['length_mm']:.2f} mm")
print(f"3dB Bandwidth: {bandwidth['bandwidth_MHz']:.0f} MHz")
Thermal management is critical for GaN PA reliability and performance. The thermal design incorporates:
Junction temperature calculation:
\[T_j = T_{ambient} + P_{dissipated} \cdot (R_{\theta,jc} + R_{\theta,cs} + R_{\theta,sa})\]where \(R_{\theta}\) represents thermal resistances from junction-to-case, case-to-sink, and sink-to-ambient.
Figure 3: Thermal simulation showing temperature distribution across PA die
The low noise amplifier employs a two-stage GaAs pHEMT design optimized for minimum noise figure. The noise figure of a two-port network is given by:
where \(F_{min}\) is minimum noise figure, \(R_n\) is equivalent noise resistance, \(Y_s\) is source admittance, and \(Y_{opt}\) is optimal source admittance.
The design process involves:
Unconditional stability is ensured through careful design:
Rollett's stability factor:
\[K = \frac{1 - |S_{11}|^2 - |S_{22}|^2 + |\Delta|^2}{2|S_{12}||S_{21}|} > 1\]and
\[|\Delta| = |S_{11}S_{22} - S_{12}S_{21}| < 1\]Stability is achieved through:
The LNA utilizes WIN Semiconductors PP15-20 0.15μm pHEMT process with the following characteristics:
| Parameter | Value | Unit |
|---|---|---|
| Minimum Noise Figure @ 28GHz | 1.8 | dB |
| Associated Gain | 12 | dB |
| fT | 90 | GHz |
| fmax | 150 | GHz |
The 8×8 uniform rectangular array (URA) provides beam steering capability through phase control of individual elements. The array factor is given by:
where \(w_{mn}\) are complex weights, \(d_x\) and \(d_y\) are element spacings, and \(k = 2\pi/\lambda\).
Key design parameters:
6-bit digital phase shifters provide 5.625° resolution:
import numpy as np
import matplotlib.pyplot as plt
class PhaseShifterArray:
def __init__(self, bits=6, freq=28e9):
self.bits = bits
self.resolution = 360 / (2**bits)
self.freq = freq
self.N = 8 # 8x8 array
def calculate_beam_weights(self, theta_steer, phi_steer):
"""Calculate phase shifts for beam steering"""
k = 2 * np.pi * self.freq / 3e8
d = 0.5 * 3e8 / self.freq # half wavelength spacing
weights = np.zeros((self.N, self.N), dtype=complex)
for m in range(self.N):
for n in range(self.N):
# Progressive phase shift
phase = -k * d * (m * np.sin(theta_steer) * np.cos(phi_steer) +
n * np.sin(theta_steer) * np.sin(phi_steer))
# Quantize to available phase states
phase_quantized = np.round(phase * 180/np.pi / self.resolution) * self.resolution
phase_quantized = phase_quantized * np.pi / 180
weights[m, n] = np.exp(1j * phase_quantized)
return weights
def calculate_array_pattern(self, theta, phi, weights):
"""Calculate array radiation pattern"""
k = 2 * np.pi * self.freq / 3e8
d = 0.5 * 3e8 / self.freq
AF = 0
for m in range(self.N):
for n in range(self.N):
phase = k * d * (m * np.sin(theta) * np.cos(phi) +
n * np.sin(theta) * np.sin(phi))
AF += weights[m, n] * np.exp(1j * phase)
return np.abs(AF)**2 / (self.N**2)**2 # Normalized
def plot_beam_pattern(self, theta_steer=30, phi_steer=0):
"""Plot 2D beam pattern"""
theta = np.linspace(-90, 90, 361) * np.pi / 180
weights = self.calculate_beam_weights(theta_steer*np.pi/180, phi_steer*np.pi/180)
pattern = np.array([self.calculate_array_pattern(t, phi_steer*np.pi/180, weights)
for t in theta])
pattern_dB = 10 * np.log10(pattern + 1e-10)
plt.figure(figsize=(10, 6))
plt.plot(theta * 180/np.pi, pattern_dB)
plt.axvline(x=theta_steer, color='r', linestyle='--', label=f'Steering angle: {theta_steer}°')
plt.xlabel('Angle (degrees)')
plt.ylabel('Normalized Pattern (dB)')
plt.title(f'8x8 Array Pattern - Steering to {theta_steer}°')
plt.grid(True, alpha=0.3)
plt.xlim([-90, 90])
plt.ylim([-40, 0])
plt.legend()
plt.show()
# Example usage
psa = PhaseShifterArray()
weights = psa.calculate_beam_weights(30*np.pi/180, 0)
print(f"Phase shifter resolution: {psa.resolution}°")
print(f"Number of phase states: {2**psa.bits}")
Array calibration compensates for element variations and mutual coupling:
Advanced beamforming techniques implemented:
Digital predistortion compensates for PA nonlinearities using an inverse model:
Memory polynomial model:
\[y(n) = \sum_{k=0}^{K-1} \sum_{m=0}^{M-1} a_{km} x(n-m)|x(n-m)|^{2k}\]where \(K\) is nonlinearity order and \(M\) is memory depth.
import numpy as np
from scipy import signal
class DigitalPredistortion:
def __init__(self, order=7, memory=4, learning_rate=0.01):
self.K = order
self.M = memory
self.learning_rate = learning_rate
self.coefficients = np.zeros((self.K, self.M), dtype=complex)
def create_basis_matrix(self, x):
"""Create basis functions for memory polynomial"""
N = len(x)
num_coeffs = self.K * self.M
X = np.zeros((N, num_coeffs), dtype=complex)
col = 0
for k in range(self.K):
for m in range(self.M):
if m <= N-1:
x_delayed = np.roll(x, m)
if m > 0:
x_delayed[:m] = 0
X[:, col] = x_delayed * np.abs(x_delayed)**(2*k)
col += 1
return X
def adapt_indirect_learning(self, x_in, y_out):
"""Indirect learning architecture"""
# Normalize signals
y_out = y_out / np.max(np.abs(y_out))
x_in = x_in / np.max(np.abs(x_in))
# Create basis matrix from PA output
X = self.create_basis_matrix(y_out)
# Least squares solution
coeffs_flat = np.linalg.lstsq(X, x_in, rcond=None)[0]
# Reshape coefficients
self.coefficients = coeffs_flat.reshape((self.K, self.M))
return self.coefficients
def predistort(self, x):
"""Apply predistortion to input signal"""
X = self.create_basis_matrix(x)
coeffs_flat = self.coefficients.flatten()
y_dpd = X @ coeffs_flat
# Limit output to prevent saturation
max_val = np.max(np.abs(y_dpd))
if max_val > 1.0:
y_dpd = y_dpd / max_val
return y_dpd
def calculate_metrics(self, x_ideal, y_actual):
"""Calculate linearization metrics"""
# Normalize for comparison
x_ideal = x_ideal / np.max(np.abs(x_ideal))
y_actual = y_actual / np.max(np.abs(y_actual))
# EVM calculation
evm = np.sqrt(np.mean(np.abs(y_actual - x_ideal)**2)) / np.sqrt(np.mean(np.abs(x_ideal)**2))
evm_percent = evm * 100
# ACLR calculation (simplified)
f, psd_out = signal.periodogram(y_actual, fs=1.0)
in_band_power = np.sum(psd_out[len(f)//4:3*len(f)//4])
out_band_power = np.sum(psd_out[:len(f)//4]) + np.sum(psd_out[3*len(f)//4:])
aclr_db = 10 * np.log10(in_band_power / out_band_power)
return {
'EVM': evm_percent,
'ACLR': aclr_db,
'NMSE': 10 * np.log10(np.mean(np.abs(y_actual - x_ideal)**2))
}
# Example: DPD adaptation
dpd = DigitalPredistortion(order=7, memory=4)
# Generate test signal (64-QAM)
N = 10000
symbols = (np.random.randint(0, 8, N) - 3.5) + 1j*(np.random.randint(0, 8, N) - 3.5)
symbols = symbols / np.max(np.abs(symbols))
# Simulate PA with compression (simplified model)
def pa_model(x, compression_point=0.7):
y = x.copy()
mask = np.abs(x) > compression_point
y[mask] = y[mask] * compression_point / np.abs(y[mask])
# Add some AM-PM distortion
y = y * np.exp(1j * 0.5 * np.abs(y)**2)
return y
# Training
pa_output = pa_model(symbols)
dpd.adapt_indirect_learning(symbols, pa_output)
# Apply DPD
symbols_dpd = dpd.predistort(symbols)
pa_output_dpd = pa_model(symbols_dpd)
# Calculate improvement
metrics_before = dpd.calculate_metrics(symbols, pa_output)
metrics_after = dpd.calculate_metrics(symbols, pa_output_dpd)
print(f"EVM before DPD: {metrics_before['EVM']:.2f}%")
print(f"EVM after DPD: {metrics_after['EVM']:.2f}%")
print(f"ACLR improvement: {metrics_after['ACLR'] - metrics_before['ACLR']:.1f} dB")
Envelope tracking (ET) dynamically adjusts the PA supply voltage to track the signal envelope, improving efficiency:
Supply voltage modulation:
\[V_{DD}(t) = V_{DD,min} + k \cdot |x(t)|\]where \(k\) is the tracking gain and \(V_{DD,min}\) is minimum supply voltage.
Implementation challenges addressed:
CFR reduces the peak-to-average power ratio (PAPR) of the transmitted signal:
Achieved 3dB PAPR reduction with <1% EVM degradation.
The RF frontend is implemented on a 10-layer PCB using Rogers RO3003 substrate:
| Layer | Type | Material | Thickness (μm) |
|---|---|---|---|
| L1 | RF Signal | Copper | 35 |
| L2 | Ground | Copper | 35 |
| L3-L4 | Power/Control | Copper | 35 |
| L5 | Ground | Copper | 35 |
| L6-L7 | Digital | Copper | 35 |
| L8 | Ground | Copper | 35 |
| L9 | RF Signal | Copper | 35 |
| L10 | Ground | Copper | 35 |
Critical layout considerations:
Comprehensive characterization performed using:
Measurement results across temperature and frequency:
Figure 4: Measured performance vs. frequency and temperature
The implemented RF frontend achieves the following performance:
Figure 5: Performance comparison with specifications
Key achievements:
| Reference | Frequency (GHz) | Pout (dBm) | PAE (%) | Technology |
|---|---|---|---|---|
| This Work | 28 | 35.2 | 40.5 | 0.15μm GaN |
| [1] IEEE TMTT 2023 | 28 | 33.5 | 38 | 0.15μm GaN |
| [2] IEEE JSSC 2023 | 28 | 34 | 35 | 0.25μm GaN |
| [3] IEEE MWCL 2022 | 26 | 34.5 | 42 | 0.1μm GaN |
System-level optimization achieved through:
This work demonstrates a comprehensive 28GHz RF frontend design achieving state-of-the-art performance for 5G NR applications. The integration of GaN power amplification, adaptive digital predistortion, and 8×8 beamforming array enables high-efficiency, linear operation supporting 256-QAM modulation. The measured results validate the design methodology and demonstrate the feasibility of commercial deployment.
Future work will focus on:
Complete S-parameter measurements available in Touchstone format:
Manufacturing files include:
Source code repositories: