Main Content

Implement HDL Optimized SVD with Backpressure Signal and HDL FIFO Block

This example shows how to implement hardware-efficient singular value decomposition (SVD) using the Square Jacobi SVD HDL Optimized block with backpressure control and an HDL FIFO block.

The Square Jacobi SVD HDL Optimized block uses the AMBA AXI handshake protocol for both input and output. The valid/ready handshake process is used to transfer data and control information. For more details about the handshake process, see Square Jacobi SVD HDL Optimized.

In this example, the data handler has an arbitary delay to emulate upstream delay. The dummy receiver emulates downstream delay. The model has two synchronous FIFO blocks inserted between the upstream data handler block and Square Jacobi SVD HDL Optimized block, as well as between the Square Jacobi SVD HDL Optimized block and the downstream receiver. For all the blocks, the downstream ready signal connects to the upstream readyIn port, and the upstream validOut signal connects to the downstream validIn port.

The Square Jacobi SVD HDL Optimized block supports backpressure control through the AMBA AXI protocol. The Synchronous FIFO block uses the HDL FIFO block with glue logic to support the AMBA AXI protocol. This example uses the FIFO blocks to demonstrate how to interface the Square Jacobi SVD HDL Optimized block and the FIFO block with backpressure control.

Define Simulation Parameters

Specify the dimension of the sample matrices, the number of input sample matrices, and the number of iterations of the Jacobi algorithm.

n = 2;
numSamples = 20;
nIterations = 6;

Specify the upstream and downstream delays.

upstreamDelay = 200;
downstreamDelay = 1000;

Generate Input A Matrices

Use the specified simulation parameters to generate the input A matrices.

rng('default');
A = randn(n,n,numSamples);

The Square Jacobi SVD HDL Optimzied block supports both real and complex inputs. Set the complexity of the input in the block mask accordingly.

% A = complex(randn(n,n,numSamples),randn(n,n,numSamples));

Select Fixed-Point Data Types

Define the desired word length.

wordLength = 32;

Use the upper bound on the singular values to define fixed-point types that will never overflow. First, use the fixed.singularValueUpperBound function to determine the upper bound on the singular values.

svdUpperBound = fixed.singularValueUpperBound(n,n,max(abs(A(:))));

Define the integer length based on the value of the upper bound, with one additional bit for the sign, another additional bit for intermediate CORDIC growth, and one more bit for intermediate growth to compute the Jacobi rotations.

additionalBitGrowth = 3;
integerLength = ceil(log2(svdUpperBound)) + additionalBitGrowth;

Compute the fraction length based on the integer length and the desired word length.

fractionLength = wordLength - integerLength;

Define the signed fixed-point data type to be 'Fixed' or 'ScaledDouble'. You can also define the type to be 'double' or 'single'.

dataType = 'Fixed';
T.A = fi([],1,wordLength,fractionLength,'DataType',dataType);
disp(T.A)
[]

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 32
        FractionLength: 26

Cast the matrix A to the signed fixed-point type.

A = cast(A,'like',T.A);

Configure Model Workspace and Run Simulation

model = 'SquareJacobiSVDFIFOModel';
open_system(model);
fixed.example.setModelWorkspace(model,'A',A,'n',n,...
    'nIterations',nIterations,'numSamples',numSamples,...
    'upstreamDelay',upstreamDelay,'downstreamDelay',downstreamDelay);
out = sim(model);

Handshake Process and Backpressure Signal

In this model, the data input rate is 200, the block latency is 577, and the dummy receiver delay is 1000. The FIFO depth is 4. Because the downstream is slower than upstream, the expected behavior is:

  1. Before the input FIFO is full, the data source rate determines the data trasaction rate. The input FIFO accepts data every 200 clocks.

  2. After the input FIFO is full, it can only accept data when the Square Jacobi SVD HDL Optimized block is ready. The data trasaction rate reduces to the block delay of 577.

  3. The Square Jacobi SVD HDL Optimized block outputs data into the output FIFO, and the dummy receiver consumes the solution every 1000 clocks. Before the output FIFO is full, the data trasaction rate is the same as the Square Jacobi SVD HDL Optimized block delay of 577.

  4. After the output FIFO is full, it can only accept data when the dummy receiver is ready. The Square Jacobi SVD HDL Optimized block waits for the dummy receiver, and the data source waits for the Square Jacobi SVD HDL Optimized block. The data trasaction rate of the signal chain reduces to every 1000 clocks.

By using backpressure signals and the handshake protocol, the upstream block waits for the downstream block without a hardcoded delay. The slowest block determines the throughput of the whole system to ensure data integrity.

Verify Output Solutions

Verify the output solutions. In these steps, "identical" means within roundoff error.

  1. Verify that U*diag(s)*V' is identical to A. relErrUSV represents the relative error between U*diag(s)*V' and A.

  2. Verify that the singular values s are identical to the floating-point SVD solution. relativeErrorS represents the relative error between s and singular values calculated by the MATLAB® svd function.

  3. Verify that U and V are unitary matrices. relativeErrorUU represents the relative error between U'*U and the identity matrix. relativeErrorVV represents the relative error between V'*V and the identity matrix.

relativeErrorUSV = zeros(numSamples,1);
relativeErrorUU = zeros(numSamples,1);
relativeErrorVV = zeros(numSamples,1);
relativeErrorS = zeros(numSamples,1);

for i = 1:numSamples
    a = A(:,:,i);
    U = out.U(:,:,i);
    V = out.V(:,:,i);
    s = out.s(:,:,i);

    % Verify U*diag(s)*V'
    if norm(double(a)) > 1
        relErrUSV = norm(double(U*diag(s)*V')-double(a))/norm(double(a));
    else
        relErrUSV = norm(double(U*diag(s)*V')-double(a));
    end
    relativeErrorUSV(i) = relErrUSV;

    % Verify s
    s_expected = svd(double(a));
    normS = norm(s_expected);
    relErrS = norm(double(s) - s_expected);
    if normS > 1
        relErrS = relErrS/normS;
    end
    relativeErrorS(i) = relErrS;

    % Verify U'*U
    U = double(U);
    UU = U'*U;
    relativeErrorUU(i) = norm(UU - eye(size(UU)));

    % Verify V'*V
    V = double(V);
    VV = V'*V;
    relativeErrorVV(i) = norm(VV - eye(size(VV)));
end
disp(['Maximum s error: ', num2str(max(relativeErrorS))]);
disp(['Maximum UsV error: ', num2str(max(relativeErrorUSV))]);
disp(['Maximum UU error: ', num2str(max(relativeErrorUU))]);
disp(['Maximum VV error: ', num2str(max(relativeErrorVV))]);
Maximum s error: 2.2305e-07
Maximum UsV error: 2.3991e-07
Maximum UU error: 3.8834e-08
Maximum VV error: 3.1142e-08

See Also

Blocks

Functions