"Stitch" Your Way Out of Logic-Analyzer Memory Limitations

Abstract

MATLAB® is a powerful tool that can be used to quickly analyze captured data from an analog-to-digital converter (ADC) output. This application note demonstrates how to use MATLAB to avoid limitations in the memory depth of logic analyzers. Three code-switching methods (basic, advanced, and reverse) are described and compared. Results for all three methods are presented.

Introduction

Characterizing high-speed analog-to-digital converters (ADCs) requires that the digital output codes be captured and analyzed. Limitations in the memory depth of logic analyzers frequently prevent capturing enough data points to create high-resolution FFTs or accurate representations of INL/DNL graphs. A simple way to circumvent this problem is to concatenate multiple sets of data with a mathematical tool such as MATLAB (Figure 1). One drawback of concatenating data is the large discontinuity, which is often present at the point between the two data sets. While the discontinuity makes little difference for INL/DNL graphs, it will wreak havoc on a high-resolution FFT (Figure 2).

Figure 1. Concatenated data reveals discontinuity between two data sets.

Figure 1. Concatenated data reveals discontinuity between two data sets.

Figure 2. a) A single 16384-point data set was captured and analyzed; b) two 8192-point data sets were captured, concatenated, and analyzed. Stitching Techniques.

Figure 2. a) A single 16384-point data set was captured and analyzed; b) two 8192-point data sets were captured, concatenated, and analyzed. Stitching Techniques.

One can eliminate discontinuities by searching for identical groups of points (typically three or four) in each data set, and 'stitching' the two data sets together at these points (Figure 3). The easiest way to accomplish this stitching is to record the last four points in the first data set and then search for an identical set of points in the second data set. This position in the second data set is called the 'stitch point.' Any data in the second data set that precedes this stitch point is discarded; the remaining portion of the second data set is attached to the first. This technique is called Basic Code Stitching, is fairly simple to implement, and executes very quickly in MATLAB.

Figure 3. Basic code stitching results in a final 'stitched' matrix.

Figure 3. Basic code stitching results in a final "stitched" matrix.

With basic code stitching, sometimes up to half of the second data set needs to be discarded in order to find a set of points that match the last four points of the first data set. Alternatively, discarding a few samples at the tail end of the first data set often helps to find a stitch point closer to the beginning of the second data set (Figure 4). However, looking for a match that discards samples from the tail end of the first data set and the front end of the second data set can be difficult to implement. This process is called Advanced Code Stitching. Finding the ideal stitching point that yields the largest resulting data set requires considerable forethought and programming skill. But if implemented properly, advanced code stitching typically yields a final data set that is at least 90% of the sum of the two smaller data sets.

Figure 4. Advanced code stitching looks for ideal stitch points which result in a final 'stitched' matrix.

Figure 4. Advanced code stitching looks for ideal stitch points which result in a final "stitched" matrix.

Stitching the second data set (Matrix B) in front of the first data set (Matrix A) is called Reverse Code Stitching and may also result in a larger data set (Figure 5). This technique, however, doubles the processing time because the stitch point must be found when data set A precedes data set B, and when data set A follows data set B. In addition, reverse code stitching typically yields minimal gains when combined with the other stitching techniques. Consequently, due to the substantial increase in processing time required for reverse code stitching, the additional code gains may not be justified on a slower PC. Table 1 details a comparison among the three code-stitching methods.

Figure 5.  Reverse code stitching doubles processing time, often with minimal code gains.

Figure 5. Reverse code stitching doubles processing time, often with minimal code gains.

Table 1. Stitch Technique Comparison Table*
Stitch technique Size of final data set Description
Data set numbers # of codes
(averaged)
% of two data sets
(averaged)
1 + 2 3 + 4 1 + 4
Concatenate† N/A 16384 100% Will produce erroneous FFT; however, INL/DNL can be extracted from this data.
Basic 11060 8192‡ 14384 11212 68.4% FFT is useable for calculating figures of merit.
Reverse 11060 8192‡ 14384 11212 68.4%
Advanced 13790 16046 16022 15286 93.3%
Advanced
+
Reverse
15427 16176 16022 15875 96.9%
*Two 8K (8192 code) data sets were stitched together using the techniques described above. To ensure accuracy, the test was repeated three times using four sets of 8192-point data (labeled 1 through 4). The resultant data from each test was averaged and is presented to the right of the test data.
†Concatenation always yields 100% of the available data.
‡Unable to stitch data sets together.

MATLAB Functional Description

The attached MATLAB code (StitchMatrices and FindStitchPoint in Appendices A and B, respectively) combines the above topics into one easy-to-use function. These functions accept two data sets (single-column matrices in MATLAB) and several input arguments that enable the advanced-/reverse-code-stitching features. The FindStitchPoint routine identifies offsets in data sets A and B. The StitchMatrices routine discards and combines data sets A and B together using the offsets from the FindStitchPoint routine. In addition, the stitch points in the final data set are recorded in the PrevStitchBins matrix for post-processing. When stitching multiple data sets together, the PrevStitchBins preserves the location of the old stitch points.

Conclusion

Stitching two sets of data together can yield acceptable results. Figure 6 depicts the FFT plot of three 8192-point data sets stitched together (five stitch points used) using the stitching techniques described above. The resulting FFT is almost identical to the 16384-point continuous data set shown in Figure 2a above.

Figure 6. Stitching codes together yields an accurate FFT plot.

Figure 6. Stitching codes together yields an accurate FFT plot.

Appendix A: StitchMatrices Routine (StitchMatrices.m)


function [StitchedMatrix, StitchBins] = StitchMatrices(MatrixA, ...
                MatrixB, StitchNumber, PrevStitchBins, ...
                AdvCodeStitchEnabled, ReverseStitchEnabled);
%Stitch Matrices Function
%Revision 1.0
%
%By Donald Schelle, May 2005
%Maxim Integrated
%160 Rio Robles
%Sunnyvale, CA, 94086
%
%This function will take two matrices (MatrixA and MatrixB), find a 
%given number (StitchNumber) of identical points in each and 
%concatenate the two matrices into one.
%
%Inputs = MatrixA, MatrixB (Data Matrices)
%           StitchNumber (Number of points to match)
%            PrevStitchBins (Bins of Previous Stitches in MatrixA)
%            AdvStitchEnabled (0 = NO, 1 = YES)
%            ReverseStitchEnabled (0 = NO, 1 = YES)
%Output = StitchedMatrix (MatrixA + MatrixB)
%            StitchBins (bins of StitchedMatrix where the two 
%                           matrices were joined.)
%
%If the matrices can not be joined the function will output a NaN
%for both the StitchedMatrix variable and the StitchBins variable
%--------------------------------------------------------------------------

%Check to see that there are at least TWO StitchNumber Points
if StitchNumber < 2,
   %Requested less than 2 stitch points
   StitchedMatrix = NaN;
   StitchBins = NaN;
   return;
end;

%Calculate Size of MatrixA and MatrixB
[SizeA, Junk] = size(MatrixA);
[SizeB, Junk] = size(MatrixB);

%Find the Stitch Points in MatrixB
[NormalA, NormalB] = FindStitchPoint(MatrixA, MatrixB, ...
                    StitchNumber, AdvCodeStitchEnabled);
%Calculate the size of the NormalStitched Matrix
NormalStitchedSize = NormalA + SizeB - NormalB + 1;
                            
%Check to see if the reverse function is enabled
if ReverseStitchEnabled == 1,
   %Find Stitch Points for Reverse Matrices
   [ReverseB, ReverseA] = FindStitchPoint(MatrixB, MatrixA, ...
                        StitchNumber, AdvCodeStitchEnabled);
   %Calculate the size of the Revered Stitched Matrix
   ReverseStitchedSize = ReverseB + SizeA - ReverseA + 1;
else
   %Set Values to defaults
   ReverseStitchedSize = NaN;       %MatrixB/A Stitch Size
   ReverseA = NaN;
   ReverseB = NaN;
end;

%Check to if it's possible to stitch two matrices
if isnan(NormalStitchedSize) & isnan(ReverseStitchedSize) == 1,
   %The two matrices could not be stitched
   StitchedMatrix = NaN;
   StitchBins = NaN;
   return;
end;

%--------------------- Normal Matrix Stitching Routine ---------------
if (NormalStitchedSize >= ReverseStitchedSize)| ...
            isnan(ReverseStitchedSize) == 1,
   %Stitch MatrixB to the end of MatrixA
   StitchedMatrix = cat(1, MatrixA(1:NormalA), MatrixB(NormalB:SizeB));
   
   %Update Stitch Bins
   if isnan(PrevStitchBins) == 1,
      %There are no previous stitch bins
      StitchBins = [NormalA, NormalA + StitchNumber - 1];
   else
      %There are previous stitch bins
      %Check for Snipped Stitches
      [SizeStitchBins, Junk] = size(PrevStitchBins);
      
      while (PrevStitchBins(SizeStitchBins, 2) > (NormalA - 1)),
         %Second Bin is snipped from matrix. Check if first bin is snipped.
         if (PrevStitchBins(SizeStitchBins, 1) > (NormalA - 1)),
            %First Bin is snipped too. Delete Bin Pair
            PrevStitchBins = PrevStitchBins(1:(SizeStitchBins-1),:);
         else
            %First Bin is not snipped but second bin is snipped
            %Shrink Stitch Size
            PrevStitchBins(SizeStitchBins, 2) = NormalA - 1;
         end;
         
         %Calculate size of new PrevStitchBin Matrix
         [SizeStitchBins, Junk] = size(PrevStitchBins);
      end;
               
      %Insert New StitchBins      
      [SizeStitchBins, Junk] = size(PrevStitchBins);
      StitchBins = PrevStitchBins;
      StitchBins(SizeStitchBins + 1, :) = ...
      [NormalA, NormalA + StitchNumber - 1];
      
      %Check to see if the last two stitches need to be combined
      [SizeStitchBins, Junk] = size(StitchBins);
      if StitchBins(SizeStitchBins,1) == ...
            (StitchBins((SizeStitchBins - 1),2) + 1),
         %Combine Stitches
         StitchBins((SizeStitchBins - 1),2) = StitchBins((SizeStitchBins),2);
         %Shorten StitchBin Matrix
         StitchBins = StitchBins(1:(SizeStitchBins - 1),:);
      end;
      
   end;   
end;


%--------------------- Reverse Matrix Stitching Routine ---------------
if (ReverseStitchedSize >= NormalStitchedSize)| ...
            isnan(NormalStitchedSize) == 1,
   %Stitch MatrixA to the end of MatrixB
   StitchedMatrix = cat(1,MatrixB(1:ReverseB), MatrixA(ReverseA:SizeA));
   
   %Update Stitch Bins
   if isnan(PrevStitchBins) == 1,
      %There are no previous stitch bins
      StitchBins = [ReverseB, ReverseB + StitchNumber - 1];
   else
      %There are previous stitch bins
      %Check for Snipped Stitches            
      while (PrevStitchBins(1,1) < (ReverseA + StitchNumber - 1)),
         %First Bin is snipped from matrix. Check if second is snipped
         if (PrevStitchBins(1,2) < (ReverseA + StitchNumber - 1)),
            %Second Bin is snipped too. Delete Bad Pair
            [SizeStitchBins, Junk] = size(PrevStitchBins);
             PrevStitchBins = PrevStitchBins(2:SizeStitchBins, :);
         else
            %Second Bin is not snipped, but first bin is snipped
            %Shrink Old Stitch Size
            PrevStitchBins(1,1) = ReverseA + StitchNumber - 1;
         end;
      end;
      
      %Offset Stitch Bins by inserted amount
      StitchBins = PrevStitchBins + ReverseB - ReverseA + 1; 
      %Make Room for new StitchBins
      [SizeStitchBins, Junk] = size(PrevStitchBins);
      StitchBins(2:SizeStitchBins+1, :) = StitchBins;
      %Insert New Stitch Bins
      StitchBins(1,:) = [ReverseB, ReverseB + StitchNumber - 1];
                  
      %Combine close stitches
      if StitchBins(1,2) == StitchBins(2,1) - 1,
         %Combine Stitches
         StitchBins(2,1) = StitchBins(1,1);
         %Shrink Stitch Bins Matrix
     [SizeStitchBins, Junk] = size(StitchBins);
         StitchBins = StitchBins(2:SizeStitchBins,:);   
      end;
      
   end;
end;

Appendix B: FindStitchPoint Routine (FindStitchPoint.m)


function [OutputBinA, OutputBinB]=FindStitchPoint(MatrixA, MatrixB, ...
                MatchNumber, AdvancedStitchFindEnabled)
%Find Stitch Points Function
%Revision 1.0
%
%By Donald Schelle, May 2005
%Maxim Integrated
%160 Rio Robles
%Sunnyvale, CA, 94086
%
%This function will find the IDEAL stitch point in Matrix B given
%the number of data points to match
%
%Inputs = MatrixA
%            MatrixB
%            Number of Records to Match
%            Advanced Stitch Find Enabled (0 = NO, 1 = YES)
%Output = (OutputBinA) End Bin of MatrixA to stitch data
%            (OutputBinB) Start Bin of Matrix B to stitch data
%
%If no bins are found, the function will output a NaN
%--------------------------------------------------------------------------

%Do argument error checking to see if there is enough arguments
if nargin < 2,
   %The user has not supplied enough arguments
   disp('Function requires TWO Matrices');
   OutputBinA = NaN;
   OutputBinB = NaN;
   return;
elseif nargin < 3,
   disp('Select a number of points to match');
   OutputBinA = NaN;
   OutputBinB = NaN;
   return;
elseif nargin == 3,
   %Advanced code stitching is NOT enabled
   OutputBinA = NaN;
   AdvancedStitchFindEnabled=0;
end;

%Ensure that Matrix A and B are single ROW matrices
[row col] = size(MatrixA);
if row > col, MatrixA = MatrixA'; end;
[row col] = size(MatrixB);
if row > col, MatrixB = MatrixB'; end;

%Determine Size of Matrices
[Junk, SizeA] = size(MatrixA);
[Junk, SizeB] = size(MatrixB);

%Initialize OutputBinB to NaN (which means that NO stitch points are found)
OutputBinB = NaN;
%Set initial size of BinA
BinA = SizeA - MatchNumber + 1;
%Initialize BinStop Variable
BinStop = SizeA-100;

%Loop to search through Matrix B numerous times.  This loop is only
%excuted once if Advanced Stitch Find is disabled.  The loop will stop when
%the 'ideal' stitch point is found
while BinA > BinStop, 
   %Stuff the Match Numbers into a separate Matrix
    MatchMatrix = MatrixA(BinA:BinA+MatchNumber - 1);
   
   %Find all bins in MatrixB that match the first number of the Match Matrix
    MatchedBins = find(MatrixB == MatchMatrix(1));
   
   %Compare the 2nd through nth number of the Match Matrix with the
   %prospective series of numbers in MatrixB
   
   %Calculate the size of the Matched Bins Matrix
   [Junk, SizeMatchedBins] = size(MatchedBins);
   
   %The advanced stitch mode optimizes search time by eliminating
   %bad stitch points that would result in the final concatenated
   %matrix being smaller than the last set of stitch points
   if isnan(OutputBinB) == 0,
      %A Stitch Point exists from a previous run.  Elimiate bad stitch points
      %Calculate critical Stitch Point
      MatrixSize = OutputBinA + (SizeB-OutputBinB) + 1;
      CriticalBin = BinA + SizeB - MatrixSize - 1;
      %Find maximum number in the MatchMatrix
      BadBin = find(MatchedBins > CriticalBin);
      %Eliminate Bad Bins (if there are any)
      if isempty(BadBin) == 0,
         MatchedBins = MatchedBins(1:BadBin(1) - 1);
      end;
      %Calculate size of new Matched Bins Matrix
      [Junk, SizeMatchedBins] = size(MatchedBins);       
   end;
   
   %loop to cycle through initial matched bins
   for i=1:SizeMatchedBins,
      %Check to make sure that there isn't a MatrixB overrun
      if (MatchedBins(i) + MatchNumber - 1) > SizeB,
         break;
      end;
      
      %Assume that next few codes will match and set StitchBinGood = true
      StitchBinGood = 1;       
      %Initialize MatchMatrixCounter
      Count = 1;
      %Cycle through MatrixB and compare Numbers with the MatchMatrix
      for j=MatchedBins(i):(MatchedBins(i) + MatchNumber - 1),
         if MatchMatrix(Count)==MatrixB(j),
            %Number is good, continue and check next number
         Count = Count + 1;
     else
            %Number is bad, break loop and try next sequence
            StitchBinGood = 0;
         break;
     end;
      end;
       
      if StitchBinGood == 1,
         %The optimal (first) stitch has been found
         %Record the End bin of MatrixA
         %Record the Start bin of MatrixB
         OutputBinA = BinA;
         OutputBinB = MatchedBins(i) + 1;
         %Calculate the size of the joined Matrix and a new BinStop#
         BinStop = OutputBinA-OutputBinB+1;
         break;
      end;  
   end;
   
   if AdvancedStitchFindEnabled == 1,
      %Advanced Stitch Find is enabled and we should make a new match
      %matrix and search for these numbers
      BinA = BinA - 1;
   else
      %Advanced Stitch Find is disabled and we should end the loop
      break;
   end;
end;

%Check to see if NO Bins Matched
if isnan(OutputBinB) == 1,
   %NO Bins matched
   OutputBinA = NaN;
end;