summaryrefslogtreecommitdiff
path: root/ADCBasedFFE.m
blob: d99371f80a09f9c88ada4808d927d03fcc841163 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
classdef (StrictDefaults) ADCBasedFFE < serdes.SerdesAbstractSystemObject & TriggeredComponent
    %ADCBasedFFE    ADC Based Feed-Forward Equalizer
    %   obj = ADCBasedFFE returns a System Object, obj, that equalizes a
    %   demuxed signal with a feed-forward equalizer.
    %
    % ADCBasedFFE methods:
    %   step - Equalizes the demuxed signal of size DemuxWidth with the FFE
    %          tap weights specified by TapWeights. The object must be
    %          additionaly driven by the demux clock.
    %          SampleOut = stepImpl(obj,SampleIn,ClockIn)
    %
    % ADCBasedFFE properties:
    %   Mode           - Equalization mode, 0=pass throught, 1=apply equalization.
    %   DemuxWidth     - Demux size of the incoming waveform.
    %   TapWeights     - FFE tap weight vector.
    %   TapWeightsPort - In Simulink enables TapWeights to be an input port.
    %   SymbolTime     - Symbol time of the system.
    %   SampleInterval - Uniform time step of the system.

    %   Copyright 2021 The MathWorks, Inc.
    
    %#codegen
    
    properties (Nontunable)
        % Mode Mode (0: pass through, 1: Apply filter)        
        Mode     =  1;
        
        %Demux Width
        DemuxWidth = 32;
    end
    properties (Hidden, SetAccess=private)
        %FFE properties
        NumberOfTaps % Number of FFE taps
        FrameOut     % Output frame
        Buffer       % Output buffer
        BlockTail    % Block convolution BlockTail
    end
    properties (Nontunable, Hidden)
        NumberOfClocks = 1;
    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
    
    properties       
        %Tap Weights
        TapWeights = 1;
    end
    
    properties(Nontunable) %port/property duality
       
        %TapWeightsPort TapWeightsPort
        %   Specify TapWeights from input port in Simulink
        TapWeightsPort (1, 1) logical = true;
    end
    
    properties (Constant, Hidden) %port/property duality
        TapWeightsSet = matlab.system.SourceSet(...
            {'PropertyOrInput', 'SystemBlock', 'TapWeightsPort', 1, 'TapWeights'}, ...
            {'Property', 'MATLAB', 'TapWeightsPort'});
    end

    properties (SetAccess = protected, GetAccess = public)
        % Power tracking results
        TotalEnergy = 0;
        CurrentPower = 0;
    end
    
    properties (SetAccess = protected, GetAccess = protected)
        % Power tracking variables
        CycleCount = 0;
        PreviousWeights;
        TapBits;
        MaxTapBits = 0;
    end

    
    methods
        % Constructor
        function obj = ADCBasedFFE(varargin)
            % Support name-value pair arguments when constructing object
            obj.BlockName = 'ADCBasedFFE';
            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)
            
            %specific properties
            obj.NumberOfTaps = length(obj.TapWeights);
            
            % Initialize convolution output and BlockTail to zero
            obj.FrameOut = zeros(obj.DemuxWidth               , 1);
            obj.Buffer = zeros(obj.DemuxWidth+obj.NumberOfTaps-1, 1);
            obj.BlockTail  = zeros(            obj.NumberOfTaps-1, 1);

            % Reset Power Vars
            obj.TotalEnergy = 0;
            obj.CurrentPower = single(0);
            obj.PreviousWeights = zeros(obj.NumberOfTaps);

            % Set bit sizes for FFE
            obj.TapBits = [10 10 11 12 12 12 0 0 12 12 11 11 11 10 10 9 9 9 8 8 8 8 8 8 8 8 8 8 8 8 7]; % 0 bits for fixed cursor and tap replaced by DFE
            obj.MaxTapBits = max(obj.TapBits);

            assert(numel(obj.TapBits) == obj.NumberOfTaps);
            assert(obj.MaxTapBits > 0);
        end
        
        function validateInputsImpl(~,waveIn)
            validateattributes(waveIn,{'numeric'},{'finite'},'','waveIn');
        end


        function [SampleOut,CurrentPowerOut] = stepImpl(obj,SampleIn,varargin)
            
            if nargin == 3
                ClockIn = varargin{1};
            else
                ClockIn = 0;
            end

            % Default to power output from the previous cycle
            CurrentPowerOut = obj.CurrentPower;
            
            if isImpulse(obj)
                %Apply FIR filter with a wrap around due to the
                %assumed nature of impulse responses waveforms.
                SamplesPerSymbol = round(obj.SymbolTime/obj.SampleInterval);
                [nrows,ncols]=size(SampleIn);
                SampleOut = zeros(size(SampleIn));
                for jj = 1:ncols
                    y1 = zeros(nrows,1);
                    for ii = 1:length(obj.TapWeights)
                        y1 = y1 + obj.TapWeights(ii)*...
                            circshift(SampleIn(:,jj),(ii-1)*SamplesPerSymbol);
                    end
                    SampleOut(:,jj)=y1;
                end

            elseif isSample(obj)
                %Triggered clock step
                ClockStep(obj,ClockIn)
                
                % On falling clock edge, process frame of samples
                if obj.PhaseFallingIndex
                    
                    if obj.Mode
                        % Convolve frame of input samples with FFE IR depending on Mode
                        obj.Buffer = conv(SampleIn, obj.TapWeights(:));
                        
                        % Add tail from previous block
                        obj.Buffer(1:obj.NumberOfTaps-1) = obj.Buffer(1:obj.NumberOfTaps-1) + obj.BlockTail;
                        
                        % Update the block convolution tail
                        obj.BlockTail = obj.Buffer(end-obj.NumberOfTaps+2:end);
                        
                        % Assign output
                        obj.FrameOut = obj.Buffer(1:obj.DemuxWidth);

                        % -- Power Computation --
                        % Count Zeros and Constant (non-zero) Multiplier Inputs
                        ZeroMask = (obj.TapWeights == 0);
                        NumberOfZeros = nnz(ZeroMask);
                        ConstantMask = (obj.PreviousWeights == obj.TapWeights);
                        NumberOfConstants = nnz(ConstantMask) - nnz(ZeroMask .* ConstantMask);

                        % Define one unit of energy as the energy required 
                        % to complete one multiplication for the largest multiplier
                        % This calculation makes the assumption that
                        % multiplier energy scales linearly with tap bit size
                        CycleEnergy = sum(obj.TapBits) / obj.MaxTapBits;

                        % 99% energy saving when an input is zero
                        CycleEnergy = CycleEnergy - 0.99 * sum(ZeroMask .* obj.TapBits) / obj.MaxTapBits;

                        % 25% energy penalty when tap weights are changing
                        % between cycles
                        % CycleEnergy = CycleEnergy + 1/(1-0.25) * (obj.NumberOfTaps - NumberOfConstants - NumberOfZeros);

                        % Scale by number of symbols processed
                        CycleEnergy = CycleEnergy * length(SampleIn);

                        % Update Outputs
                        obj.TotalEnergy = obj.TotalEnergy + CycleEnergy;
                        obj.CurrentPower = single(CycleEnergy);

                        % Update energy tracking state
                        obj.PreviousWeights = obj.TapWeights;

                        CurrentPowerOut = obj.CurrentPower;

                        % Assertions
                        assert(obj.TotalEnergy >= 0);
                        assert(obj.CurrentPower >= 0);
                    else
                        obj.FrameOut = SampleIn(:);
                        obj.BlockTail = zeros(size(obj.BlockTail));
                    end
                    
                end % obj.PhaseFallingIndex > 0
                
                % Assign outputs
                SampleOut = obj.FrameOut;
            end
            
        end

        function releaseImpl(obj)
            % Print the total energy used by FFE
            if obj.TotalEnergy > 0
                fprintf('Total Energy: %.2f\n', obj.TotalEnergy);
            end
        end

        function [sz_1,sz_2] = getOutputSizeImpl(obj)
            % Return size for each output port
            sz_1 = [obj.DemuxWidth 1];
            sz_2 = [1 1];
        end
        function [c1,c2] = isOutputFixedSizeImpl(~)
            c1 = true;
            c2 = true;
        end
        function [dt1,dt2] = getOutputDataTypeImpl(obj)
            dt1 = propagatedInputDataType(obj,1);
            dt2 = 'single';
        end
        function [c1,c2] = isOutputComplexImpl(~)
            c1 = false;
            c2 = false;
        end
        
        function resetImpl(obj)
            % Initialize / reset discrete-state properties
        end
        
        %% Simulink functions
        function icon = getIconImpl(~)
            % Define icon for System block
            icon = sprintf('ADC\nBased\nFFE');
        end
        function [name1,name2,name3] = getInputNamesImpl(~)
            name1 = 'Sample';
            name2 = sprintf('Demux\nClock');
            name3 = 'Taps';
        end        
        function [name1,name2] = getOutputNamesImpl(~)
            name1 = 'Sample';
            name2 = 'CurrentPower';
        end
        function num = getNumInputsImpl(obj)
            if isSample(obj)
                num = 2;
            else
                num = 1;
            end
        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',{'Mode','DemuxWidth','TapWeights','TapWeightsPort',...
                'SymbolTime','SampleInterval'});
        end
    end    
end