diff options
Diffstat (limited to 'ADCBasedDFE.m')
| -rw-r--r-- | ADCBasedDFE.m | 377 |
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 |
