Optimize Data Types for an FPGA with DSP Slices

This example shows how to use the addSpecification method of the fxpOptimizationOptions class to achieve better mapping for product blocks on DSP slices for FPGA targets. Use addSpecification to specify known data types in a system. After specifying these known parameters, when you optimize data types in the system, the optimization process does not change the specified block parameter data type.

Many FPGA boards have specific multiply-accumulate hardware accelerators, called DSP slices that speed up the execution of signal processing functions. DSP slices vary in size depending on the vendor. To gain the hardware acceleration benefits of DSP slices, it is common in FPGA design to map multiply and accumulate operations in the algorithm onto these slices.

In this example, optimize data types for 3 DSP families of Xilinx® boards, as well as for a generic 18x18 bit input. Use the addSpecification method to achieve a good mapping between the Product blocks in the design and the target DSP slices.

This example makes the following assumptions:

  1. Only Product and Gain blocks are targeted for mapping to DSP slices. You can handle other blocks in a similar fashion.

  2. Product blocks have only 2 inputs.

  3. Driver blocks (blocks that precede Product or Gain blocks) have OutDataTypeStr as a parameter.

  4. Only blocks that do not have the HDL property DSPStyle set to off are targeted.

Instrument the Model and Collect Ranges

To begin, open the system for which you want to optimize data types. In this example, data types are optimized for an automatic gain control algorithm.

model = 'mQAMAGC';
sud = [model '/Automatic Gain Control'];
open_system(model);

Initialize the QAM Tx subsystem.

initQAM;

Create a structure array that describes the properties of the target DSP slice. The dspStyle function included in this example provides information about common Xilinx® DSP slices including DSP48A1 (18x18 bit signed), DSP48E1 (18x25 bit signed), and DSP48E2 (18x27 bit signed). It also provides an example of a generic 18x18 signed DSP slice.

dspStyle = getDSPStyle('DSP48E2');

In this example, only Product and Gain blocks are targeted for mapping to DSP slices. Find all the Product and Gain blocks in the system under design that do not have the HDL block property DSPStyle set to off. For more information, see DSPStyle (HDL Coder).

productBlocks = find_system(sud,'LookUnderMasks','on','BlockType','Product');
hasDSPOff = cellfun(@(x)(isequal(hdlget_param(x,'DSPStyle'),'off')), productBlocks);
productDSP = productBlocks(~hasDSPOff);

gainBlocks = find_system(sud,'LookUnderMasks','on','BlockType','Gain');
hasDSPOff = cellfun(@(x)(isequal(hdlget_param(x,'DSPStyle'),'off')), productBlocks);
gainDSP = gainBlocks(~hasDSPOff);

Enable instrumentation to log minimum, maximum, and overflow data during simulation for Product blocks and driver blocks that preceed Product blocks. Simulate the model to collect ranges.

c = DataTypeWorkflow.Converter(sud,'TopModel',model);
c.CurrentRunName = 'RangeCollection';
c.simulateSystem('MinMaxOverflowLogging','MinMaxAndOverflow');

Get Specifications for Product Blocks

For efficient mapping of Product blocks to available DSP slices, consider the range requirements of the Product blocks. Create a structure array to store simulation minimum and maximum values for Product blocks collected during the range collection run.

specs = struct('Block',productDSP{1},'Drivers',[],'Min',[],'Max',[]); %#ok<*SAGROW>
r = c.results(c.CurrentRunName,@(x)(strcmp(x.ResultName,productDSP{1})));
specs.Min = r.SimMin;
specs.Max = r.SimMax;
predecessorBlocks = predecessors(productDSP{1});
for pIndex = 1:numel(predecessorBlocks)
    pBlkObj = get_param(predecessorBlocks{pIndex},'Object');
    specs.Drivers(pIndex) = pBlkObj.Handle;
    r = c.results(c.CurrentRunName,@(x)(strcmp(x.ResultName,pBlkObj.getFullName())));
    specs.Min(pIndex+1) = r.SimMin;
    specs.Max(pIndex+1) = r.SimMax;
end

Store these known parameter specifications for the Product blocks in a Simulink.Simulation.BlockParameter object.

bpProductBlock = Simulink.Simulation.BlockParameter.empty(0,3);

fout = fi(max([abs(specs.Min(1)) abs(specs.Max(1))]),dspStyle.sout,dspStyle.wout);
bpProductBlock(1) = Simulink.Simulation.BlockParameter(specs.Block, ...
    'OutDataTypeStr', ...
    sprintf('fixdt(%i,%i,%i)',dspStyle.sout,dspStyle.wout,fout.FractionLength));

Assign the largest type to the largest range of the driver blocks. This ensures that the largest data type available in the target hardware is applied to the block with the largest range requirement.

dMax1 = max([abs(specs.Min(2)) abs(specs.Max(2))]);
dMax2 = max([abs(specs.Min(3)) abs(specs.Max(3))]);
if dMax1 < dMax2
    win_1 = dspStyle.win_1;
    sin_1 = dspStyle.sin_1;
    win_2 = dspStyle.win_2;
    sin_2 = dspStyle.sin_2;
    if dspStyle.win_1 >= dspStyle.win_2
        win_1 = dspStyle.win_2;
        sin_1 = dspStyle.sin_2;
        win_2 = dspStyle.win_1;
        sin_2 = dspStyle.sin_1;
    end
else
    win_1 = dspStyle.win_2;
    sin_1 = dspStyle.sin_2;
    win_2 = dspStyle.win_1;
    sin_2 = dspStyle.sin_1;
    if dspStyle.win_1 >= dspStyle.win_2
        win_1 = dspStyle.win_1;
        sin_1 = dspStyle.sin_1;
        win_2 = dspStyle.win_2;
        sin_2 = dspStyle.sin_2;
    end
end

Get specifications for blocks preceeding Product blocks. Note that this example assumes that Product blocks have two inputs.

fin1 = fi(dMax1, sin_1, win_1);
blkObj = get_param(specs.Drivers(1), 'Object');
bpProductBlock(2) = Simulink.Simulation.BlockParameter(blkObj.getFullName, ...
    'OutDataTypeStr', ...
    sprintf('fixdt(%i, %i, %i)', sin_1, win_1, fin1.FractionLength));

fin2 = fi(dMax2, sin_2, win_2);
blkObj = get_param(specs.Drivers(2), 'Object');
bpProductBlock(3) = Simulink.Simulation.BlockParameter(blkObj.getFullName, ...
    'OutDataTypeStr', ...
    sprintf('fixdt(%i, %i, %i)', sin_2, win_2, fin2.FractionLength));

Get Specifications for Gain Blocks

Store known parameter specifications for the Gain blocks in a Simulink.Simulation.BlockParameter object.

bpGainBlock = Simulink.Simulation.BlockParameter.empty(0,3);
specs = struct('Block',gainDSP{1},'Drivers',[],'Min',[],'Max',[]); %#ok<*SAGROW>
r = c.results(c.CurrentRunName,@(x)(strcmp(x.ResultName,gainDSP{1})));
specs.Min = r.SimMin;
specs.Max = r.SimMax;
predecessorBlocks = predecessors(gainDSP{1});
pBlkObj = get_param(predecessorBlocks{1},'Object');
specs.Drivers(1) = pBlkObj.Handle;
r = c.results(c.CurrentRunName,@(x)(strcmp(x.ResultName,pBlkObj.getFullName())));
specs.Min(2) = r.SimMin;
specs.Max(2) = r.SimMax;

Get specifications for the output of Gain blocks.

fout = fi(max(abs([specs.Min(1) specs.Max(1)])),dspStyle.sout,dspStyle.wout);
bpGainBlock(1) = Simulink.Simulation.BlockParameter(gainDSP{1}, ...
    'OutDataTypeStr', ...
    sprintf('fixdt(%i, %i, %i)',dspStyle.sout,dspStyle.wout,fout.FractionLength));

Get specifications for the blocks preceeding Gains blocks and assign this to the first configuration of the Simulink.Simulation.BlockParameter object bpGainBlock.

blkObj = get_param(specs.Drivers(1),'Object');
fin = fi(max(abs([specs.Min(2) specs.Max(2)])),dspStyle.sin_1,dspStyle.win_1);
bpGainBlock(2) = Simulink.Simulation.BlockParameter(blkObj.getFullName, ...
    'OutDataTypeStr', ...
    sprintf('fixdt(%i,%i,%i)',dspStyle.sin_1,dspStyle.win_1,fin.FractionLength));

Get specifications for the Gain parameter of the system under design and assign this value to the second configuration of bpGainBlock.

paramValue = str2double(get_param(sud,'AGC_Gain'));
fParam = fi(paramValue,dspStyle.sin_2,dspStyle.win_2);
bpGainBlock(3) = Simulink.Simulation.BlockParameter(gainDSP{1}, ...
    'ParamDataTypeStr', ...
    sprintf('fixdt(%i,%i,%i)',dspStyle.sin_2,dspStyle.win_2,fParam.FractionLength));

Define Constraints and Tolerances

Create an fxpOptimizationOptions object to define constraints and tolerances. Specify allowed word lengths of 8 bits to 32 bits.

options = fxpOptimizationOptions('AllowableWordLengths',8:2:32);

Use the addTolerance method to define tolerances for the differences between the original behavior of the system and the behavior using the optimized fixed-point data types.

addTolerance(options,sud,1,'RelTol',1e-2);
addTolerance(options,sud,2,'RelTol',1e-2);
addTolerance(options,sud,1,'AbsTol',1e-3);
addTolerance(options,sud,2,'AbsTol',1e-3);

Use the addSpecification method to define specifications for the Product and Gain blocks.

addSpecification(options,'BlockParameter',bpProductBlock); % set the specifications for the product block
addSpecification(options,'BlockParameter',bpGainBlock); % set the specifications for the gain block
showSpecifications(options);
    Index          Name                            BlockPath                            Value       
    _____    ________________    _____________________________________________    __________________

      1      OutDataTypeStr      mQAMAGC/Automatic Gain Control/LoopGain          'fixdt(1, 45, 53)'
      2      ParamDataTypeStr    mQAMAGC/Automatic Gain Control/LoopGain          'fixdt(1,27,35)'  
      3      OutDataTypeStr      mQAMAGC/Automatic Gain Control/LoopGainDriver    'fixdt(1,18,16)'  
      4      OutDataTypeStr      mQAMAGC/Automatic Gain Control/Product           'fixdt(1,45,43)'  
      5      OutDataTypeStr      mQAMAGC/Automatic Gain Control/ProductDriverA    'fixdt(1, 27, 25)'
      6      OutDataTypeStr      mQAMAGC/Automatic Gain Control/ProductDriverB    'fixdt(1, 18, 17)'

Optimize Fixed-Point Data Types

Use the fxpopt function to run the optimization. The software analyzes ranges of objects in the system under design and the constraints specified in the fxpOptimizationOptions object to apply heterogeneous data types to your system while minimizing the total bit width. Known parameter specifications included using the addSpecification method is not affected by the optimization process.

result = fxpopt(model,sud,options);
	+ Checking for unsupported constructs.
	+ Preprocessing
	+ Modeling the optimization problem
		- Constructing decision variables
	+ Running the optimization solver
		- Evaluating new solution: cost 200, does not meet the tolerances.
		- Evaluating new solution: cost 250, does not meet the tolerances.
		- Evaluating new solution: cost 300, does not meet the tolerances.
		- Evaluating new solution: cost 350, does not meet the tolerances.
		- Evaluating new solution: cost 400, does not meet the tolerances.
		- Evaluating new solution: cost 450, does not meet the tolerances.
		- Evaluating new solution: cost 500, meets the tolerances.
		- Updated best found solution, cost: 500
		- Evaluating new solution: cost 494, meets the tolerances.
		- Updated best found solution, cost: 494
		- Evaluating new solution: cost 492, meets the tolerances.
		- Updated best found solution, cost: 492
		- Evaluating new solution: cost 490, does not meet the tolerances.
		- Evaluating new solution: cost 486, does not meet the tolerances.
		- Evaluating new solution: cost 490, meets the tolerances.
		- Updated best found solution, cost: 490
		- Evaluating new solution: cost 488, meets the tolerances.
		- Updated best found solution, cost: 488
		- Evaluating new solution: cost 478, meets the tolerances.
		- Updated best found solution, cost: 478
		- Evaluating new solution: cost 474, meets the tolerances.
		- Updated best found solution, cost: 474
		- Evaluating new solution: cost 470, meets the tolerances.
		- Updated best found solution, cost: 470
		- Evaluating new solution: cost 466, meets the tolerances.
		- Updated best found solution, cost: 466
		- Evaluating new solution: cost 462, meets the tolerances.
		- Updated best found solution, cost: 462
		- Evaluating new solution: cost 458, meets the tolerances.
		- Updated best found solution, cost: 458
		- Evaluating new solution: cost 452, meets the tolerances.
		- Updated best found solution, cost: 452
		- Evaluating new solution: cost 450, meets the tolerances.
		- Updated best found solution, cost: 450
		- Evaluating new solution: cost 448, does not meet the tolerances.
		- Evaluating new solution: cost 444, does not meet the tolerances.
		- Evaluating new solution: cost 448, meets the tolerances.
		- Updated best found solution, cost: 448
		- Evaluating new solution: cost 446, meets the tolerances.
		- Updated best found solution, cost: 446
		- Evaluating new solution: cost 436, meets the tolerances.
		- Updated best found solution, cost: 436
		- Evaluating new solution: cost 432, meets the tolerances.
		- Updated best found solution, cost: 432
		- Evaluating new solution: cost 428, meets the tolerances.
		- Updated best found solution, cost: 428
		- Evaluating new solution: cost 424, meets the tolerances.
		- Updated best found solution, cost: 424
		- Evaluating new solution: cost 420, meets the tolerances.
		- Updated best found solution, cost: 420
		- Evaluating new solution: cost 416, meets the tolerances.
		- Updated best found solution, cost: 416
		- Evaluating new solution: cost 410, meets the tolerances.
		- Updated best found solution, cost: 410
		- Evaluating new solution: cost 408, meets the tolerances.
		- Updated best found solution, cost: 408
		- Evaluating new solution: cost 406, does not meet the tolerances.
		- Evaluating new solution: cost 402, does not meet the tolerances.
		- Evaluating new solution: cost 406, meets the tolerances.
		- Updated best found solution, cost: 406
		- Evaluating new solution: cost 404, meets the tolerances.
		- Updated best found solution, cost: 404
		- Evaluating new solution: cost 394, meets the tolerances.
		- Updated best found solution, cost: 394
		- Evaluating new solution: cost 390, meets the tolerances.
		- Updated best found solution, cost: 390
		- Evaluating new solution: cost 386, meets the tolerances.
		- Updated best found solution, cost: 386
		- Evaluating new solution: cost 382, meets the tolerances.
		- Updated best found solution, cost: 382
		- Evaluating new solution: cost 378, meets the tolerances.
		- Updated best found solution, cost: 378
		- Evaluating new solution: cost 374, meets the tolerances.
		- Updated best found solution, cost: 374
		- Evaluating new solution: cost 368, does not meet the tolerances.
		- Evaluating new solution: cost 372, meets the tolerances.
		- Updated best found solution, cost: 372
		- Evaluating new solution: cost 370, does not meet the tolerances.
		- Evaluating new solution: cost 366, does not meet the tolerances.
		- Evaluating new solution: cost 370, meets the tolerances.
		- Updated best found solution, cost: 370
		- Evaluating new solution: cost 368, meets the tolerances.
		- Updated best found solution, cost: 368
		- Evaluating new solution: cost 358, does not meet the tolerances.
		- Evaluating new solution: cost 364, meets the tolerances.
		- Updated best found solution, cost: 364
		- Evaluating new solution: cost 360, meets the tolerances.
		- Updated best found solution, cost: 360
		- Evaluating new solution: cost 356, meets the tolerances.
		- Updated best found solution, cost: 356
		- Evaluating new solution: cost 352, meets the tolerances.
		- Updated best found solution, cost: 352
		- Evaluating new solution: cost 348, meets the tolerances.
		- Updated best found solution, cost: 348
		- Evaluating new solution: cost 342, does not meet the tolerances.
		- Evaluating new solution: cost 346, meets the tolerances.
		- Updated best found solution, cost: 346
	+ Optimization has finished.
		- Neighborhood search complete.
		- Maximum number of iterations completed.
	+ Fixed-point implementation that met the tolerances found.
		- Total cost: 346
		- Maximum absolute difference: 0.006126
		- Use the explore method of the result to explore the implementation.

Use the explore method of the OptimizationResult object, result, to launch the Simulation Data Inspector and explore the design.

explore(result)
ans = 

  OptimizationSolution with properties:

             Cost: 346
             Pass: 1
    MaxDifference: 0.0061
            RunID: 28653
          RunName: {'solution_a5b8ac6dbb7bff4abce5d3519508121c380dbb14_1'}