diff options
Diffstat (limited to 'LMSUpdate.m')
| -rw-r--r-- | LMSUpdate.m | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/LMSUpdate.m b/LMSUpdate.m new file mode 100644 index 0000000..d51984d --- /dev/null +++ b/LMSUpdate.m @@ -0,0 +1,400 @@ +classdef (StrictDefaults) LMSUpdate < serdes.SerdesAbstractSystemObject & TriggeredComponent & handle + %LMSUpdate - Output Updated Weigths based on Given Samples + % obj = ADCBasedFFE returns a System Object, obj, that equalizes a + % demuxed signal with a feed-forward equalizer. + % + % LMSUpdate methods: + % step - Updates internal FFE Taps, DFE Taps, and Dlevs using + % sign-sign LMS Algorithm and outputs them + % + % LMSUpdate properties: + % NumPreTaps - Number of FFE Pre-Taps + % NumPostTaps - Number of FFE Post-Taps + % + % Init_MuFFE - Initial Learning Rate for updating FFE Taps + % Init_MuDFE - Initial Learning Rate for updating DFE Tap + % Init_MuDlev - Initial Learning Rate for updating Dlevs + % + % Init_Dlevs - Starting Dlev Values for better Adaptation + % + % StartFFEAdaptTime - After Going over this many Symbol Frames, + % start running ssLMS for FFE + % StartDFEAdaptTime - After Going over this many Symbol Frames, + % start running ssLMS for DFE + % StartDlevAdaptTime - After Going over this many Symbol Frames, + % start running ssLMS for Dlevs + % + % NumSymbolFramesAdaptation - After this many Symbol Frames, + % End ssLMS Adaptation + % + % LearningRatesAdjustTimes - Halve LMS Learning Rates after reaching + % this many Symbol Frames + + + %#codegen + properties (Nontunable) + NumPreTaps = 6; + NumPostTaps = 24; + Init_MuFFE = 0.005; + Init_MuDFE = 0.0025; + Init_MuDlev = 0.0005; + + Init_Dlevs = [-0.2 -0.075 0.075 0.2]; + + StartFFEAdaptTime = 150; + StartDFEAdaptTime = 250; + + WindowSize = 64; + + NumSymbolFramesAdaptation = 96000/64; + LearningRatesAdjustTimes = [500, 850, 1250]; + + TogglingAlgorithm {mustBeMember(TogglingAlgorithm, ... + {'base', 'allzeros', 'mincoeff', 'leveraged', 'threshold', 'informedMinC', 'marginLock'} ... + )} = 'base'; + end + properties (Hidden, SetAccess=private) + Mu + MuDFE + MuDlev + + TapWeights + DFE_Tap + + InitializedTaps + + TogglingAlgoObj + FFE_TapsMask + + IterCount + + Dlev_0p5 + Dlev_0p16 + Dlev_m0p16 + Dlev_m0p5 + + Votes + + Vote_DFE_Tap + + Votes_Dlev_0p5 + Votes_Dlev_0p16 + Votes_Dlev_m0p16 + Votes_Dlev_m0p5 + + AdcLSB + AdcDynamicRange + AdcResolution + + PrevAdcSamples_D0 + PrevAdcSamples_D1 + PrevAdcSamples_D2 + + PrevDFEOutput + PrevDesiredOutput + + ErrorVec + + Adapting + end + properties (Nontunable, Hidden) + NumberOfClocks = 1; + end + + properties (SetAccess = immutable, Nontunable, Hidden) + IsLinear = true; + IsTimeInvariant = true; + end + + methods + % Constructor + function obj = LMSUpdate(varargin) + % Support name-value pair arguments when constructing object + obj.BlockName = 'LMSUpdate'; + 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 = public) + function setLearningRates(obj, newMu, newMuDFE, newMuDlev) + obj.Mu = newMu; + obj.MuDFE = newMuDFE; + obj.MuDlev = newMuDlev; + end + end + methods(Access = protected) + %% Common functions + function setupImpl(obj) + setupClock(obj) + + obj.Mu = obj.Init_MuFFE; + obj.MuDFE = obj.Init_MuDFE; + obj.MuDlev = obj.Init_MuDlev; + + obj.TapWeights = [zeros(1,obj.NumPreTaps) 1 zeros(1,obj.NumPostTaps)]; + obj.DFE_Tap = 0; + + obj.InitializedTaps = false; + + obj.FFE_TapsMask = ones(1, obj.NumPreTaps + 1 + obj.NumPostTaps); + + obj.IterCount = 0; + + % hard-coded initial dlevs + obj.Dlev_0p5 = obj.Init_Dlevs(4); + obj.Dlev_0p16 = obj.Init_Dlevs(3); + obj.Dlev_m0p16 = obj.Init_Dlevs(2); + obj.Dlev_m0p5 = obj.Init_Dlevs(1); + + obj.Votes = zeros(1, length(obj.TapWeights)); + + obj.Vote_DFE_Tap = 0; + + obj.Votes_Dlev_0p5 = 0; + obj.Votes_Dlev_0p16 = 0; + obj.Votes_Dlev_m0p16 = 0; + obj.Votes_Dlev_m0p5 = 0; + + obj.AdcDynamicRange = 0.35; + obj.AdcResolution = 6; + obj.AdcLSB = 2 * obj.AdcDynamicRange / (2^obj.AdcResolution - 1); + + obj.PrevAdcSamples_D0 = zeros(1,64).'; + obj.PrevAdcSamples_D1 = zeros(1,64).'; + obj.PrevAdcSamples_D2 = zeros(1,64).'; + + obj.PrevDFEOutput = zeros(1,64).'; + obj.PrevDesiredOutput = zeros(1,64).'; + + obj.ErrorVec = zeros(1,64); + + obj.Adapting = 1; + + obj.TogglingAlgoObj = TapTogglingFramework(obj.TogglingAlgorithm, obj); + + % Print Config: + fprintf("[LMSUpdate] Toggling Algo: %s\n", obj.TogglingAlgorithm); + fprintf("[LMSUpdate] NumPreTaps=%.0f, NumPostTaps=%.0f\n", obj.NumPreTaps, obj.NumPostTaps); + fprintf("[LMSUpdate] WindowSize=%.0f, StartFFEAdaptTime=%.0f, StartDFEAdaptTime=%.0f\n", obj.WindowSize, obj.StartFFEAdaptTime, obj.StartDFEAdaptTime); + end + + function [UpdatedTapWeights, UpdatedDFETap, UpdatedDlevs, Adapting, Dbg] = stepImpl(obj, AdcSamples, DFE_Output, Desired_Output, ClockIn, Init_FFE_Taps, Init_DFE_Tap) + if obj.DFE_Tap == 0 && Init_DFE_Tap ~= 0 + obj.DFE_Tap = Init_DFE_Tap; + end + + if ~obj.InitializedTaps + obj.TapWeights = Init_FFE_Taps; + obj.InitializedTaps = true; + end + + %Triggered clock step + ClockStep(obj,ClockIn); + + + % Convert Decisions to ADC Samples + Sampled_Desired_Output = Desired_Output; + + for i = 1:length(Desired_Output) + % Adjust Desired Outputs (Decision Symbols) to Current Dlevs + if Desired_Output(i) > 0.4 + Sampled_Desired_Output(i) = obj.Dlev_0p5; + elseif Desired_Output(i) < 0.3 && Desired_Output(i) > 0 + Sampled_Desired_Output(i) = obj.Dlev_0p16; + elseif Desired_Output(i) > -0.3 && Desired_Output(i) < 0 + Sampled_Desired_Output(i) = obj.Dlev_m0p16; + else + Sampled_Desired_Output(i) = obj.Dlev_m0p5; + end + + end + + + if obj.PhaseFallingIndex && obj.Adapting + obj.IterCount = obj.IterCount + 1; + + % Extend ADC Samples to use later for updating FFE Taps + ExtendedAdcSamples = [obj.PrevAdcSamples_D1.' obj.PrevAdcSamples_D0.']; + + obj.ErrorVec = Sampled_Desired_Output.' - DFE_Output.'; + + if ismember(obj.IterCount, obj.WindowSize * obj.LearningRatesAdjustTimes) + obj.Mu = obj.Mu / 10; + obj.MuDFE = obj.MuDFE / 10; + obj.MuDlev = obj.MuDlev / 10; + end + + % FFE Taps Adaptation (First) + if obj.IterCount > obj.StartFFEAdaptTime * obj.WindowSize + + for m = 1:(length(DFE_Output)) + err = obj.ErrorVec(m); + + for l = 1:length(obj.TapWeights) + extended_index = length(AdcSamples) + m - (l-1); + obj.Votes(l) = obj.Votes(l) + sign(err)*sign(ExtendedAdcSamples(extended_index)); + end + end + + if mod(obj.IterCount, obj.WindowSize) == 0 + obj.Votes = obj.FFE_TapsMask .* obj.Votes; + DeltaWeights = 2 * obj.Mu * sign(obj.Votes); + DeltaWeights(obj.NumPreTaps+1) = 0; % fix the main cursor at 1 + DeltaWeights(obj.NumPreTaps+2) = 0; % fix the first cursor to 0 (let DFE handle it) + obj.TapWeights = obj.TapWeights + DeltaWeights; + + obj.Votes = zeros(1, length(obj.TapWeights)); + end + + end + + % DFE Tap Adaptation (Second) + if obj.IterCount > obj.StartDFEAdaptTime * obj.WindowSize + for m = 1:(length(DFE_Output)) + err = obj.ErrorVec(m); + prev_dfe_dec = obj.PrevDesiredOutput(m); + + obj.Vote_DFE_Tap = obj.Vote_DFE_Tap + sign(err)*sign(prev_dfe_dec); + end + + if mod(obj.IterCount, obj.WindowSize) == 0 + obj.DFE_Tap = obj.DFE_Tap + obj.MuDFE * sign(obj.Vote_DFE_Tap); + obj.Vote_DFE_Tap = 0; + end + end + + % Dlevs Adaptation (Always On) + if obj.IterCount >= obj.WindowSize + + for m = 1:(length(DFE_Output)) + e = obj.ErrorVec(m); + + if Sampled_Desired_Output(m) == obj.Dlev_0p5 + obj.Votes_Dlev_0p5 = obj.Votes_Dlev_0p5 + sign(e); + elseif Sampled_Desired_Output(m) == obj.Dlev_0p16 + obj.Votes_Dlev_0p16 = obj.Votes_Dlev_0p16 + sign(e); + elseif Sampled_Desired_Output(m) == obj.Dlev_m0p16 + obj.Votes_Dlev_m0p16 = obj.Votes_Dlev_m0p16 + sign(e); + elseif Sampled_Desired_Output(m) == obj.Dlev_m0p5 + obj.Votes_Dlev_m0p5 = obj.Votes_Dlev_m0p5 + sign(e); + end + end + + if mod(obj.IterCount, obj.WindowSize) == 0 + muRate = obj.MuDlev; + if obj.IterCount >= (obj.StartFFEAdaptTime) * obj.WindowSize + muRate = muRate / 2; + end + + + obj.Dlev_0p5 = obj.Dlev_0p5 - muRate*sign(obj.Votes_Dlev_0p5); + obj.Dlev_0p16 = obj.Dlev_0p16 - muRate*sign(obj.Votes_Dlev_0p16); + obj.Dlev_m0p16 = obj.Dlev_m0p16 - muRate*sign(obj.Votes_Dlev_m0p16); + obj.Dlev_m0p5 = obj.Dlev_m0p5 - muRate*sign(obj.Votes_Dlev_m0p5); + + obj.Votes_Dlev_0p5 = 0; + obj.Votes_Dlev_0p16 = 0; + obj.Votes_Dlev_m0p16 = 0; + obj.Votes_Dlev_m0p5 = 0; + end + + end + + % Latch Samples + obj.PrevAdcSamples_D2 = obj.PrevAdcSamples_D1; + obj.PrevAdcSamples_D1 = obj.PrevAdcSamples_D0; + obj.PrevAdcSamples_D0 = AdcSamples; + + obj.PrevDFEOutput = DFE_Output; + obj.PrevDesiredOutput = Sampled_Desired_Output; + + % End adaptation after certain period of time + if obj.IterCount >= round(obj.NumSymbolFramesAdaptation * obj.WindowSize) + obj.Adapting = 0; + end + + obj.FFE_TapsMask = obj.TogglingAlgoObj.getMask(obj.TapWeights .* obj.FFE_TapsMask, obj); + end + + %obj.TapWeights = obj.TapWeights .* obj.FFE_TapsMask; + UpdatedTapWeights = obj.TapWeights .* obj.FFE_TapsMask; + + UpdatedDFETap = obj.DFE_Tap; + + UpdatedDlevs = [obj.Dlev_m0p5 obj.Dlev_m0p16 obj.Dlev_0p16 obj.Dlev_0p5]; + + Adapting = obj.Adapting; + Dbg = obj.FFE_TapsMask'; + end + + function releaseImpl(obj) + % Print the final tap weights + fprintf('[LMSUpdate] Final FFE Weights: '); + for k = 1:numel(obj.TapWeights) + fprintf(' %6.9f', obj.TapWeights(k)); + end + fprintf('\n'); + fprintf("[LMSUpdate] Final DFE Tap: %6.16f", obj.DFE_Tap); + fprintf('\n'); + fprintf('[LMSUpdate] Final Dlevs: '); + fprintf(' %6.9f %6.9f %6.9f %6.9f', ... + obj.Dlev_m0p5, obj.Dlev_m0p16, obj.Dlev_0p16, obj.Dlev_0p5); + fprintf('\n'); + fprintf('[LMSUpdate] Final FFE Taps Mask: '); + for k = 1:numel(obj.FFE_TapsMask) + fprintf(' %1.0f', obj.FFE_TapsMask(k)); + end + fprintf('\n'); + end + + %% Simulink functions + function icon = getIconImpl(~) + % Define icon for System block + icon = sprintf('LMS\nUpdate'); + end + function [name1,name2,name3,name4,name5,name6] = getInputNamesImpl(~) + name1 = 'AdcSamples'; + name2 = 'DFE_Output'; + name3 = 'Desired_Output'; + name4 = 'Demux Clock'; + name5 = 'Init FFE Taps'; + name6 = 'Init DFE Tap'; + end + function [name1, name2, name3, name4, name5] = getOutputNamesImpl(~) + name1 = sprintf('Updated\nTapWeights'); + name2 = sprintf('Updated\nDFE Tap'); + name3 = sprintf('Updated\nDlevs'); + + name4 = 'Adapting'; + name5 = 'Dbg'; + end + function num = getNumInputsImpl(~) + num = 6; + end + end + methods(Static, Access=protected) + function group = getPropertyGroupsImpl(~) + % Define property section(s) for System block dialog + group = matlab.system.display.SectionGroup(... + 'Title','Main',... + 'PropertyList',{'NumPreTaps','NumPostTaps',... + 'Init_MuFFE','Init_MuDFE','Init_MuDlev','Init_Dlevs',... + 'StartFFEAdaptTime','StartDFEAdaptTime','WindowSize',... + 'NumSymbolFramesAdaptation','LearningRatesAdjustTimes', ... + 'TogglingAlgorithm'}); + end + end +end
\ No newline at end of file |
