diff options
| author | Davit Grigoryan <[email protected]> | 2026-04-09 12:25:24 -0700 |
|---|---|---|
| committer | Davit Grigoryan <[email protected]> | 2026-04-22 02:37:26 -0700 |
| commit | 14ca50edb249d5f4ef621903259d955deb3a2fc3 (patch) | |
| tree | a1798764b97c2c351ea9c8ab1422886e9488d18b /TimeInterleavedADC.m | |
Diffstat (limited to 'TimeInterleavedADC.m')
| -rw-r--r-- | TimeInterleavedADC.m | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/TimeInterleavedADC.m b/TimeInterleavedADC.m new file mode 100644 index 0000000..6b6f834 --- /dev/null +++ b/TimeInterleavedADC.m @@ -0,0 +1,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 |
