summaryrefslogtreecommitdiff
path: root/ADCBasedDFE.m
diff options
context:
space:
mode:
Diffstat (limited to 'ADCBasedDFE.m')
-rw-r--r--ADCBasedDFE.m377
1 files changed, 377 insertions, 0 deletions
diff --git a/ADCBasedDFE.m b/ADCBasedDFE.m
new file mode 100644
index 0000000..eca2d81
--- /dev/null
+++ b/ADCBasedDFE.m
@@ -0,0 +1,377 @@
+classdef (StrictDefaults) ADCBasedDFE < serdes.SerdesAbstractSystemObject & TriggeredComponent
+ % ADCBasedDFE ADC-based Decision Feedback Equalizer
+ % obj = ADCBasedDFE returns a System Object, obj, that applies a
+ % single-tap decision-feedback equalization to the input samples as
+ % well as makes data decisions and calculates the signal-to-noise
+ % ratio.
+ %
+ % ADCBasedDFE methods:
+ % step - Equalizes the demuxed input samples accordingly to a
+ % single-tap DFE. Data symbol decisions are made,
+ % signal-to-noise ratio calculated and PAM thresholds
+ % determined. The following is an example of the inputs and
+ % outputs of the method:
+ % [SampleOut,DecisionOut,SNR,TapOut,PAMThresholdn1,...
+ % PAMThreshold0,PAMThreshold1] = stepImpl(obj,SampleIn,ClockIn)
+ %
+ % ADCBasedDFE properties
+ % Mode - DFE Mode, 0=off, 1=fixed, 2=adapt in Init
+ % DemuxWidth - Width of the input samples
+ % TapWeight -
+ % TapWeightPort -
+ % SymbolTime - Symbol time of system
+ % Modulation - Modulation scheme: 2=NRZ, 4=PAM4
+ % SampleInterval - Uniform time step of the system
+
+ % Copyright 2021-2024 The MathWorks, Inc.
+
+ %#codegen
+
+ properties (Nontunable)
+
+ %Mode Mode (0: Pass through, 1:Fixed, 2:Adapt)
+ % When set to 2, adaptation occurs in impulse-based analysis only
+ Mode = 2;
+
+ %Demux word size
+ DemuxWidth = 32;
+ end
+ properties (Hidden, SetAccess=private)
+
+ %ADCBasedDFE properties
+ DataInternal % Internal slicing decisions, Data internal
+ DataOut % Output decisions
+ SampleOut % Output samples
+ SignalLevels % Expected signal levels
+ DecisionSymbols % Decision symbol levels
+ AbsoluteSample % Absolute value of current sample
+ AbsoluteEyeHeight % Absolute average eye height
+ AveragingWindow % threshold recovery average window
+ SignalNoiseRatio % SNR
+ SignalBuffer % signal buffer for SNR calculations
+ SignalEstimate % Signal energy used for SNR calculations
+ NoiseEstimate % Noise energy used for SNR calculations
+ PAMThresholds; % PAM Thresholds
+
+ buf_size = 512; % Signal buffer size for SNR calculation
+ end
+ properties(Hidden,Nontunable)
+ NumberOfClocks = 1;
+ end
+ properties
+
+ %Tap Weight
+ TapWeight = 0;
+ end
+
+ properties(Nontunable) %port/property duality
+ %TapWeightPort TapWeightPort
+ % Specify TapWeight from input port in Simulink
+ TapWeightPort (1, 1) logical = true;
+ end
+
+ properties (Constant, Hidden) %port/property duality
+ TapWeightSet = matlab.system.SourceSet(...
+ {'PropertyOrInput', 'SystemBlock', 'TapWeightPort', 1, 'TapWeight'}, ...
+ {'Property', 'MATLAB', 'TapWeightPort'});
+ end
+
+ properties (SetAccess = immutable, Nontunable, Hidden)
+ IsLinear = true;
+ IsTimeInvariant = true;
+ end
+ properties (Nontunable,Hidden)
+ %Input Waveform Type
+ % Set the input wave type as one of 'Sample' | 'Impulse' |
+ % 'Waveform'. The default is 'Sample'.
+ WaveType = 'Sample';
+ end
+ properties(Hidden, Constant)
+ WaveTypeSet = matlab.system.StringSet({'Sample','Impulse','Waveform'});
+ end
+ methods
+ % Constructor
+ function obj = ADCBasedDFE(varargin)
+ % Support name-value pair arguments when constructing object
+ obj.BlockName = 'ADCBasedDFE';
+ setProperties(obj,nargin,varargin{:})
+ end
+ end
+ methods (Hidden)
+ % The below methods, getAMIParameters, getAMIInputNames and
+ % getAMIOutputNames are for use only within the serdesDesigner App
+ % and will not influence the AMI parameters in Simulink whatsoever.
+ function amiParameters = getAMIParameters(~)
+ amiParameters = {};
+ end
+ function names = getAMIInputNames(~)
+ names = {};
+ end
+ function names = getAMIOutputNames(~)
+ names = {};
+ end
+ end
+ methods (Access = protected, Hidden)
+ function val = isSample(obj)
+ val = strcmpi(obj.WaveType,'Sample');
+ end
+ function val = isImpulse(obj)
+ val = strcmpi(obj.WaveType,'Impulse');
+ end
+ function val = ModeIsOff(obj)
+ val = obj.Mode==double(0);
+ end
+ function val = ModeIsFixed(obj)
+ val = obj.Mode==double(1);
+ end
+ function val = ModeIsAdapt(obj)
+ val = obj.Mode==double(2);
+ end
+ end
+ methods(Access = protected)
+ %% Common functions
+ function setupImpl(obj)
+
+ setupClock(obj)
+
+ % Initialize signal and decision levels and SNR buffers
+ % Slicer thereshold will be between expected signal levels
+ obj.SignalNoiseRatio = NaN;
+ obj.SignalEstimate = 0;
+ obj.NoiseEstimate = inf;
+ if obj.Modulation ==4 % PAM4
+ obj.SignalLevels = [-0.5, -0.5/3, 0.5/3, 0.5];
+ obj.DecisionSymbols = [-0.5, -0.5/3, 0.5/3, 0.5];
+ obj.AbsoluteEyeHeight = 0.5*2/3;
+ obj.SignalBuffer = nan(obj.buf_size, 2);
+ obj.PAMThresholds = [-1/3 0 1/3];
+ else %if obj.Modulation == 2 % NRZ
+ obj.SignalLevels = [-0.5, 0.5 0 0]; % NRZ signal levels
+ obj.DecisionSymbols = [-0.5, 0.5 0 0]; % NRZ decision output levels
+ obj.AbsoluteEyeHeight = 0.5;
+ obj.SignalBuffer = nan(obj.buf_size, 1);
+ obj.PAMThresholds = [0 0 0];
+ end
+
+ obj.AveragingWindow = 1024; % Average window for signal detection
+ obj.AbsoluteSample = 0.0;
+
+ % Initialize output decisions and samples
+ obj.DataInternal = zeros(obj.DemuxWidth+1, 1); %data internal
+ obj.DataOut = zeros(obj.DemuxWidth, 1);
+ obj.SampleOut = zeros(obj.DemuxWidth, 1);
+
+ end
+
+ function validateInputsImpl(~,waveIn)
+ validateattributes(waveIn,{'numeric'},{'finite'},'','waveIn');
+ end
+
+ function [SampleOut,DecisionOut,SNR,TapOut,PAMThresholdn1,...
+ PAMThreshold0,PAMThreshold1] = stepImpl(obj,SampleIn,DLEVs,ClockIn)
+
+ if isImpulse(obj)
+ %1) convert to pulse
+ %2) mueller-muller CDR
+ %3) determine DFE tap
+ %4) apply DFE
+ %5) prep outputs, output tap
+
+ %Convert to pulse
+ SamplesPerSymbol = round(obj.SymbolTime/obj.SampleInterval);
+ pulse = impulse2pulse(SampleIn(:,1), SamplesPerSymbol, obj.SampleInterval);
+ pulseLength = length(pulse);
+
+ %Determine sampling time with hula hoop algorithm
+ nclock = round(pulseRecoverClock( pulse, SamplesPerSymbol*2 ))-1;
+
+
+ %Estimate tap from pulse response
+ if ModeIsAdapt(obj)
+ for kk = 1: length(obj.TapWeight)
+ %Determine Tap values
+ obj.TapWeight(kk) = pulse(mod(nclock+kk*SamplesPerSymbol-1,pulseLength)+1);
+ end
+ end
+
+ %Apply tap to impulse (not to crosstalk)
+ if ~ModeIsOff(obj)
+ for kk = 1: length(obj.TapWeight)
+ ndx = mod(nclock+kk*SamplesPerSymbol - SamplesPerSymbol/2 -1,pulseLength)+1;
+ SampleIn(ndx,1) = SampleIn(ndx,1) + obj.TapWeight(kk)/obj.SampleInterval;
+ end
+ end
+
+ % Assign outputs
+ SampleOut = SampleIn;
+ DecisionOut = NaN;
+ SNR = -Inf;
+
+ obj.PAMThresholds = (-(obj.Modulation-2)/2:(obj.Modulation-2)/2) * pulse(nclock)/(obj.Modulation-1);
+ else %if isSample(obj)
+ ClockStep(obj,ClockIn)
+
+ % On falling clock edge, process frame of samples
+ if obj.PhaseFallingIndex > 0
+
+ obj.SignalLevels = DLEVs;
+
+ % move last decision to be first decision for next iteration
+ obj.DataInternal(1) = obj.DataInternal(end);
+
+ % Apply DFE contribution, feedback based on tap-weight, if Mode=1
+ for ii = 1 : obj.DemuxWidth
+ if obj.Mode ~= 0
+ % Apply DFE contribution
+ obj.SampleOut(ii) = SampleIn(ii) - obj.TapWeight(1) * obj.DataInternal(ii);
+ else % Samples are unchanged
+ obj.SampleOut(ii) = SampleIn(ii);
+ end
+
+ % Slice the signal, by picking the signal level that has
+ % smallest euclidian distance to current signal levels
+ [~, didx] = min(abs(obj.SampleOut(ii) - obj.SignalLevels));
+ % output decision is corresponding output signal level
+ obj.DataInternal(ii+1) = obj.DecisionSymbols(didx);
+
+ % Sample-by-sample threshoddld recovery, assume symmetry between +/-
+ obj.AbsoluteSample = abs(obj.SampleOut(ii));
+
+ % Running Average for eye height
+ obj.AbsoluteEyeHeight = obj.AbsoluteEyeHeight + (abs(obj.SampleOut(ii)) - obj.AbsoluteEyeHeight)/obj.AveragingWindow/2;
+
+ if obj.Modulation == 4
+ if obj.AbsoluteSample > obj.AbsoluteEyeHeight
+ % Add signal to SNR buffer
+ obj.SignalBuffer(:, 2) = circshift(obj.SignalBuffer(:, 2), 1);
+ obj.SignalBuffer(1, 2) = obj.AbsoluteSample;
+ elseif obj.AbsoluteSample < obj.AbsoluteEyeHeight
+ % Add signal to SNR buffer
+ obj.SignalBuffer(:, 1) = circshift(obj.SignalBuffer(:, 1), 1);
+ obj.SignalBuffer(1, 1) = obj.AbsoluteSample;
+ end
+ %Calculate PAM4 thresholds
+ obj.PAMThresholds = (obj.SignalLevels(1:end-1)+obj.SignalLevels(2:end))/2;
+
+ elseif obj.Modulation ==2
+ % Add signal to SNR buffer
+ obj.SignalBuffer = circshift(obj.SignalBuffer, 1);
+ obj.SignalBuffer(1) = obj.AbsoluteSample;
+
+ %Calculate PAM Threshold
+ obj.PAMThresholds(2) = (obj.SignalLevels(1)+obj.SignalLevels(2))/2;
+ else
+ error('nope')
+ end
+
+ end % for ii = 1 : obj.DemuxWidth + 1
+
+ end % obj.PhaseFallingIndex > 0
+
+ % Calculate SNR value
+ if obj.Modulation == 4
+
+ %Mean of signal levels
+ u1 = mean(obj.SignalBuffer(:,1));
+ u2 = mean(obj.SignalBuffer(:,2));
+
+ obj.SignalEstimate = (u1^2 + u2^2)/2;
+ obj.NoiseEstimate = mean([ obj.SignalBuffer(:,2) - u2;...
+ obj.SignalBuffer(:,1) - u1].^2);
+ else
+ %Signal mean
+ u = mean(obj.SignalBuffer);
+
+ obj.SignalEstimate = u^2;
+ obj.NoiseEstimate = mean( (obj.SignalBuffer(:,1) - u).^2 );
+ end
+ obj.SignalNoiseRatio = 10*log10(obj.SignalEstimate/obj.NoiseEstimate);
+
+ % Assign outputs
+ obj.DataOut = obj.DataInternal(2:obj.DemuxWidth + 1);
+ SampleOut = obj.SampleOut(1:obj.DemuxWidth);
+ DecisionOut = obj.DataOut(1:obj.DemuxWidth);
+
+ if isnan(obj.SignalNoiseRatio(1))
+ SNR = -1;
+ else
+ SNR = obj.SignalNoiseRatio(1);
+ end
+ end
+ TapOut = obj.TapWeight(1);
+ PAMThresholdn1 = obj.PAMThresholds(1);
+ PAMThreshold0 = obj.PAMThresholds(2);
+ PAMThreshold1 = obj.PAMThresholds(3);
+ end
+ function [sz_1,sz_2,sz_3,sz_4,sz_5,sz_6,sz_7] = getOutputSizeImpl(obj)
+ % Return size for each output port
+ sz_1 = [obj.DemuxWidth 1];
+ sz_2 = [obj.DemuxWidth 1];
+ sz_3 = [1 1];
+ sz_4 = [1 1];
+ sz_5 = [1 1];
+ sz_6 = [1 1];
+ sz_7 = [1 1];
+ end
+ function [c1,c2,c3,c4,c5,c6,c7] = isOutputFixedSizeImpl(~)
+ c1 = true;
+ c2 = true;
+ c3 = true;
+ c4 = true;
+ c5 = true;
+ c6 = true;
+ c7 = true;
+ end
+ function [dt1,dt2,dt3,dt4,dt5,dt6,dt7] = getOutputDataTypeImpl(obj)
+ dt1 = propagatedInputDataType(obj,1);
+ dt2 = dt1;
+ dt3 = dt1;
+ dt4 = dt1;
+ dt5 = dt1;
+ dt6 = dt1;
+ dt7 = dt1;
+ end
+ function [c1,c2,c3,c4,c5,c6,c7] = isOutputComplexImpl(~)
+ c1 = false;
+ c2 = false;
+ c3 = false;
+ c4 = false;
+ c5 = false;
+ c6 = false;
+ c7 = false;
+ end
+
+ function resetImpl(~)
+ % Initialize / reset discrete-state properties
+ end
+
+ %% Simulink functions
+ function icon = getIconImpl(~)
+ % Define icon for System block
+ icon = sprintf('ADC\nBased\nDFE');
+ end
+ function [name1,name2,name3,name4] = getInputNamesImpl(~)
+ name1 = 'Sample';
+ name2 = 'DLEVs';
+ name3 = sprintf('Demux\nClock');
+ name4 = 'Tap';
+ end
+ function [name1,name2,name3,name4,name5,name6,name7] = getOutputNamesImpl(~)
+ name1 = 'Sample';
+ name2 = 'Decision';
+ name3 = 'SNR';
+ name4 = 'Tap';
+ name5 = 'ThresholdLower';
+ name6 = 'ThresholdCenter';
+ name7 = 'ThresholdUpper';
+ end
+ function num = getNumInputsImpl(obj)
+ if isSample(obj)
+ num = 3;
+ else
+ num = 1;
+ end
+ end
+ end
+
+end \ No newline at end of file