Troubleshooting 64-QAM DFE-NLMS Equalizer( High BER and Issues with Learning)
Show older comments
Hi all,
I’m working on a 64-QAM receiver simulation in Simulink (aiming for HDL Coder) and I’m having a lot of trouble getting the DFE-NLMS equalizer to converge. The BER is stuck high and the constellation looks terrible no matter what parameters I tweak. I suspect there is a logical bug in how I’m handling the feedback/update loop.
I want to restructure the equalizer block to do this:
- Use the first 14 symbols of the incoming Tx stream as a training sequence to get the initial coefficients.
- After these 14 symbols, I want to check the error magnitude. If it falls below a specific threshold, it should switch to decision-directed mode and pass the cleaned data to the demodulator.
My questions are:
- What is the best way to write this state/threshold logic in a MATLAB Function block so it stays HDL-compatible?
- How should I handle the packet/frame length? Do I need a dedicated counter to track the 14-symbol training window and the data payload, or is there a better way to do this in a streaming HDL design?
If anyone has a template or code snippet of a working DFE with this kind of training/decision logic, I’d really appreciate the help.
Thanks!
function [eq_out, err, w_ff_out] = dfe_nlms_hdl(rx_sample, tx_ref, train_enable)
%#codegen
% HDL-friendly DFE-NLMS equalizer for normalized 64-QAM
%
% Inputs:
% rx_sample : complex received sample
% tx_ref : complex reference symbol (properly delayed)
% train_enable : 1 => training mode, 0 => decision-directed mode
%
% Outputs:
% eq_out : equalizer output
% err : error signal
% w_ff_out : feed-forward weights (for monitoring)
% ------------------------------------------------------------
% Parameters
% ------------------------------------------------------------
N_ff = 10;
N_fb = 15;
mu_train = fi(0.002, 0, 16, 15);
mu_dd = fi(0.0007, 0, 16, 15);
delta = fi(0.05, 0, 16, 15);
% ------------------------------------------------------------
% Numeric type templates
% ------------------------------------------------------------
zero_r = cast(0, 'like', real(rx_sample));
one_r = cast(1, 'like', real(rx_sample));
zero_c = complex(zero_r, zero_r);
one_c = complex(one_r, zero_r);
% ------------------------------------------------------------
% Wider type for power accumulation (prevents overflow)
% ------------------------------------------------------------
if isa(zero_r, 'embedded.fi')
T_power = fi(0, 1, 24, 12); % signed, 24-bit, 12 fractional bits
zero_p = cast(0, 'like', T_power);
else
zero_p = zero_r;
end
% ------------------------------------------------------------
% Force inputs into the same complex type template
% ------------------------------------------------------------
rx_c = complex(cast(real(rx_sample), 'like', zero_r), ...
cast(imag(rx_sample), 'like', zero_r));
rx_c = cast(rx_c, 'like', zero_c);
tx_c = complex(cast(real(tx_ref), 'like', zero_r), ...
cast(imag(tx_ref), 'like', zero_r));
tx_c = cast(tx_c, 'like', zero_c);
% ------------------------------------------------------------
% Persistent states
% ------------------------------------------------------------
persistent ff_delay_line fb_delay_line w_ff w_fb
if isempty(ff_delay_line)
ff_delay_line = repmat(zero_c, N_ff, 1);
fb_delay_line = repmat(zero_c, N_fb, 1);
w_ff = repmat(zero_c, N_ff, 1);
w_fb = repmat(zero_c, N_fb, 1);
% Initialize center tap
w_ff(8) = one_c;
end
% ------------------------------------------------------------
% Update feed-forward delay line
% ------------------------------------------------------------
for i = N_ff:-1:2
ff_delay_line(i) = ff_delay_line(i - 1);
end
ff_delay_line(1) = rx_c;
% ------------------------------------------------------------
% Feed-forward FIR output
% ------------------------------------------------------------
y_ff = zero_c;
for i = 1:N_ff
prod_ff = cast(w_ff(i) * ff_delay_line(i), 'like', zero_c);
y_ff = cast(y_ff + prod_ff, 'like', zero_c);
end
% ------------------------------------------------------------
% Feedback FIR output
% ------------------------------------------------------------
y_fb = zero_c;
for i = 1:N_fb
prod_fb = cast(w_fb(i) * fb_delay_line(i), 'like', zero_c);
y_fb = cast(y_fb + prod_fb, 'like', zero_c);
end
% ------------------------------------------------------------
% DFE output
% ------------------------------------------------------------
eq_out = cast(y_ff - y_fb, 'like', zero_c);
% ------------------------------------------------------------
% Desired symbol selection
% ------------------------------------------------------------
if train_enable
desired = cast(tx_c, 'like', zero_c);
mu_curr = mu_train;
else
re_dec = slicer_64qam_axis(real(eq_out), zero_r);
im_dec = slicer_64qam_axis(imag(eq_out), zero_r);
desired = complex(cast(re_dec, 'like', zero_r), ...
cast(im_dec, 'like', zero_r));
desired = cast(desired, 'like', zero_c);
mu_curr = mu_dd;
end
% ------------------------------------------------------------
% Error
% ------------------------------------------------------------
err = cast(desired - eq_out, 'like', zero_c);
% ------------------------------------------------------------
% NLMS normalization power
% p_ff = sum(|x|^2)
% Use wider type to avoid overflow
% ------------------------------------------------------------
p_ff = zero_p;
for i = 1:N_ff
re_i = cast(real(ff_delay_line(i)), 'like', zero_p);
im_i = cast(imag(ff_delay_line(i)), 'like', zero_p);
re2 = cast(re_i * re_i, 'like', zero_p);
im2 = cast(im_i * im_i, 'like', zero_p);
mag2 = cast(re2 + im2, 'like', zero_p);
p_ff = cast(p_ff + mag2, 'like', zero_p);
end
norm_factor = cast(p_ff + cast(delta, 'like', zero_p), 'like', zero_p);
% ------------------------------------------------------------
% Adaptation step
% step = (mu / (||x||^2 + delta)) * err
% ------------------------------------------------------------
step_gain = cast(cast(mu_curr, 'like', zero_p) / norm_factor, ...
'like', zero_r);
step = cast(step_gain * err, 'like', zero_c);
% ------------------------------------------------------------
% Update feed-forward weights
% w_ff = w_ff + step * conj(ff_delay_line)
% ------------------------------------------------------------
for i = 1:N_ff
dw_ff = cast(step * conj(ff_delay_line(i)), 'like', zero_c);
w_ff(i) = cast(w_ff(i) + dw_ff, 'like', zero_c);
end
% ------------------------------------------------------------
% Update feedback weights
% y = y_ff - y_fb => gradient sign is negative for FB path
% w_fb = w_fb - step * conj(fb_delay_line)
% ------------------------------------------------------------
for i = 1:N_fb
dw_fb = cast(step * conj(fb_delay_line(i)), 'like', zero_c);
w_fb(i) = cast(w_fb(i) - dw_fb, 'like', zero_c);
end
% ------------------------------------------------------------
% Update feedback delay line using desired symbol
% ------------------------------------------------------------
for i = N_fb:-1:2
fb_delay_line(i) = fb_delay_line(i - 1);
end
fb_delay_line(1) = desired;
% ------------------------------------------------------------
% Optional output
% ------------------------------------------------------------
w_ff_out = w_ff;
end
function y = slicer_64qam_axis(x, zero_r)
%#codegen
% Axis slicer for normalized square 64-QAM
% Levels: ±1/7, ±3/7, ±5/7, ±7/7
x_r = cast(x, 'like', zero_r);
lvl1 = cast(1/7, 'like', zero_r);
lvl3 = cast(3/7, 'like', zero_r);
lvl5 = cast(5/7, 'like', zero_r);
lvl7 = cast(1, 'like', zero_r);
th0 = cast(0, 'like', zero_r);
th2 = cast(2/7, 'like', zero_r);
th4 = cast(4/7, 'like', zero_r);
th6 = cast(6/7, 'like', zero_r);
thm2 = cast(-2/7, 'like', zero_r);
thm4 = cast(-4/7, 'like', zero_r);
thm6 = cast(-6/7, 'like', zero_r);
if x_r < thm6
y = -lvl7;
elseif x_r < thm4
y = -lvl5;
elseif x_r < thm2
y = -lvl3;
elseif x_r < th0
y = -lvl1;
elseif x_r < th2
y = lvl1;
elseif x_r < th4
y = lvl3;
elseif x_r < th6
y = lvl5;
else
y = lvl7;
end
y = cast(y, 'like', zero_r);
end
1 Comment
Kranti Balaga
on 6 Jul 2026
Hi, Can you clarify which example you are referring to?
Thanks
Answers (0)
Categories
Find more on HDL Code Generation in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!