summaryrefslogtreecommitdiff
path: root/TimeInterleavedADC.m
blob: 6b6f834a8a86320fc0a5af2572070ca65e1e4cb5 (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
classdef (StrictDefaults) TimeInterleavedADC < serdes.SerdesAbstractSystemObject & TriggeredComponent
    % TimeInterleavedADC     Time Interleaved Analog-to-Digital Converter
    %   obj = TimeInterleavedADC returns a System Object, obj, that samples
    %   the input waveform by a bank of ADCs so as to relax the sample
    %   capture timing requirements for faster data rates.
    %
    % TimeInterleavedADC methods:
    %   step - Samples the waveform by a set of ADCs according to the analog
    %          clock inputs. The object returns a vector of output samples
    %          and a digital version of the clock as follows:
    %          [SampleOut,ClockDigital] = step(obj,WaveIn,ClockAnalog)
    %
    % TimeInterleavedADC properties:
    %   DynamicRange   - Peak dynamic range of each ADC in volts.
    %   Resolution     - Nominal resolution of each ADC in bits.
    %   NumberOfClocks - Number of clocks or number of ADCs in the system.
    %                    This value must be coordinated with the size of
    %                    the input analog clock.
    %   SampleInterval - Uniform time step of the waveform.

    %   Copyright 2021 The MathWorks, Inc.

    %#codegen

    properties (Nontunable)

        % Dynamic range (V peak)
        DynamicRange  = inf;

        % Nominal resolution (bits)
        Resolution =   inf;

        %Number of ADCs
        NumberOfClocks = 4;
    end

    properties (Hidden, SetAccess=private)

        %ADC properties
        InputPrevious        % Previous input
        InputCurrent         % Current  input
        Buffer               % Buffered samples
        SampleOut            % Output   samples
        ClockDigitalInternal % Output   clock
        PhaseReleaseIndex;   % Clock phase to release sample from buffer to output
        LSB                  % Least Significant Bit (LSB) size, V
    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 (SetAccess = immutable, Nontunable, Hidden)
        IsLinear = true;
        IsTimeInvariant = true;
    end

    properties(Hidden, Constant)
        WaveTypeSet = matlab.system.StringSet({'Sample','Impulse','Waveform'});
    end

    methods
        % Constructor
        function obj = TimeInterleavedADC(varargin)
            % Support name-value pair arguments when constructing object
            obj.BlockName = 'TimeInterleavedADC';
            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.
        % They are required by the serdes.SerdesAbstractSystemObject.
        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
    end
    methods(Access = protected)
        %% Common functions
        function setupImpl(obj)

            setupClock(obj)

            % Calculate LSB size
            if isinf(obj.Resolution)
                obj.LSB = 0;
            else
                obj.LSB = 2 * obj.DynamicRange / (2^obj.Resolution - 1);
            end

            % Initialize buffers and indexes
            obj.InputPrevious   = 0;
            obj.InputCurrent    = 0;
            obj.PhaseReleaseIndex = 2;

            % Initialize sample buffer to zero
            obj.Buffer = zeros(obj.NumberOfClocks, 1);

            % Initialize sample output to half LSB
            obj.SampleOut = (obj.LSB/2) * ones(obj.NumberOfClocks, 1);

            % Initialize clock output to -1
            obj.ClockDigitalInternal = -ones(obj.NumberOfClocks, 1);
        end

        function validateInputsImpl(~,waveIn)
            validateattributes(waveIn,{'numeric'},{'finite'},'','waveIn');
        end

        function [SampleOut,ClockDigital] = stepImpl(obj,WaveIn,varargin)
            %[SampleOut,ClockDigital] = stepImpl(obj,WaveIn,ClockAnalog)

            if nargin == 3
                ClockAnalog = varargin{1};
            else
                ClockAnalog = 0;
            end

            if isSample(obj)

                ClockStep(obj,ClockAnalog)

                % Update buffers
                obj.InputPrevious  = obj.InputCurrent ;
                obj.InputCurrent   = WaveIn      ;

                % On rising clock edge, trigger corresponding ADC
                if obj.PhaseRisingIndex > 0

                    % Get buffer release phase (phase after current rising edge phase)
                    obj.PhaseReleaseIndex  = mod(obj.PhaseRisingIndex, obj.NumberOfClocks) + 1;

                    % Interpolation index from clock waveform (fraction of UI)
                    mu = obj.ClockPrevious(obj.PhaseRisingIndex) / (obj.ClockPrevious(obj.PhaseRisingIndex) - obj.ClockCurrent(obj.PhaseRisingIndex));

                    % Interpolate sample at clock zero-crossing
                    VoltageAtClock = (1 - mu) * obj.InputPrevious + mu * obj.InputCurrent;

                    %Inject input offset voltage and Gain offset here.
                    %Bandwidth offset would require N filters applied to
                    %InputCurrent (and InputPrevious) and then index the
                    %correct waveform here.

                    %Place sample into buffer
                    obj.Buffer(obj.PhaseRisingIndex) = VoltageAtClock;

                    % Quantize and release buffer to output for the next clock phase
                    obj.SampleOut(obj.PhaseReleaseIndex) = obj.quant(obj.Buffer(obj.PhaseReleaseIndex));

                end % obj.PhaseRisingIndex > 0

                % Output clock is a square wave +1/-1, avoids 0 values
                obj.ClockDigitalInternal = sign(ClockAnalog - eps);

                % Assign outputs
                SampleOut  = obj.SampleOut ;
                ClockDigital = obj.ClockDigitalInternal;
            else
                % Assign outputs
                SampleOut  = WaveIn;
                ClockDigital = 0;
            end

        end
        function [sz_1,sz_2] = getOutputSizeImpl(obj)
            % Return size for each output port
            sz_1 = [obj.NumberOfClocks 1];
            sz_2 = [obj.NumberOfClocks 1];
        end
        function [c1,c2] = isOutputFixedSizeImpl(~)
            c1 = true;
            c2 = true;
        end
        function [dt1,dt2] = getOutputDataTypeImpl(obj)
            dt1 = propagatedInputDataType(obj,1);
            dt2 = propagatedInputDataType(obj,2);
        end
        function [c1,c2] = isOutputComplexImpl(~)
            c1 = false;
            c2 = false;
        end
        function resetImpl(~)
            % Initialize / reset discrete-state properties
        end

        %% Simulink functions
        function icon = getIconImpl(~)
            % Define icon for System block
            icon = "Time\nInterleaved\nADC";
        end
        function [name1,name2] = getInputNamesImpl(~)
            name1 = 'Wave';
            name2 = sprintf('Analog\nClock');
        end
        function [name1,name2] = getOutputNamesImpl(~)
            name1 = 'Samples';
            name2 = sprintf('Digital\nClock');
        end
        function num = getNumInputsImpl(obj)
            if isSample(obj)
                num = 2;
            else
                num = 1;
            end
        end
        function s_q = quant(obj, s)
            % Quantize a sample

            % Infinite resolution: quantization OFF (bypass mode)
            if isinf(obj.Resolution)

                % 1. clip to +/- dynamic range
                s_q = obj.clip(s);

                % Finite resolution: quantization ON
            else

                % 1. clip to +/- dynamic range
                % 2. shift up by dynamic range
                % 3. scale by 1/LSB
                % 4. quantize
                % 5. scale back by LSB
                % 6. shift down by dynamic range
                s_q = -obj.DynamicRange + obj.LSB * round(     ...
                    (obj.clip(s) + obj.DynamicRange) / obj.LSB);
            end

        end

        function s_lim = clip(obj, s)
            % Clip a sample to +/- dynamic range
            s_lim = max(-obj.DynamicRange, min(obj.DynamicRange, s));
        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',{'DynamicRange','Resolution','NumberOfClocks',...
                'SampleInterval'});
        end
    end
end