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
|