MATLAB Examples

FPGA Floating-Point Library IP Mapping

This example illustrates the floating-point workflow that integrates IP libraries provided by vendors, such as Altera and Xilinx. For more information on how to map designs to floating-point libraries, refer to the FPGA Floating-Point Library Mapping documentation.

Contents

Introduction

Implementing designs with floating-point arithmetic enable you to model with higher precision and wider dynamic range and saves time by skipping floating-point to fixed-point conversion. This is particularly beneficial for model-based design, where high-level algorithms are modeled with floating-point and does not have implementation timing details, such as pipelining and timing constraints. However, they are necessary to map operations to floating-point IP modules. HDL Coder automatically optimizes and implements your designs with these timing details and provides interfaces for you to adjust them. Floating-point math is finally implemented by integrating with floating-point IP modules from vendor libraries.

Mapping designs to floating-point IP libraries

This Field-Oriented Control (FOC) algorithm example demonstrates the basic steps in this workflow to map designs to floating-point libraries. See Field-Oriented control example for details about this application.

This model uses single-precision and contains blocks that perform basic math operators, such as adders, multipliers, comparators, and complex sin and cos functions.

Signal rates in this model are modeled at 20 $\mu s$ or 50 KHz only. Notice that this model contains only the numerical implementation, and doesn't have any FPGA implementation timing details, such as operation latencies. All numerical operations, including sin and cos functions, compute in a single sample time-step.

open_system('hdlcoderFocCurrentSingleTargetHdl');
open_system('hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/Sine_Cosine');

Choosing an IP library

In order to map to a vendor floating-point library, set the FPGA device.

hdlset_param('hdlcoderFocCurrentSingleTargetHdl', 'SynthesisToolChipFamily', 'Arria 10');
hdlset_param('hdlcoderFocCurrentSingleTargetHdl', 'SynthesisTool', 'Altera Quartus II');

Setup target library tools.

hdlsetuptoolpath('ToolName', 'Altera Quartus II','ToolPath', quartuspath);
hdlsetuptoolpath('ToolName', 'XILINX ISE','ToolPath', isepath);

quartuspath and isepath return synthesis tool paths in our environment. Refer to hdlsetuptoolpath documentation for how to setup tools in your environment.

The first step is to choose a vendor library. For Xilinx devices, you can use 'XILINXLOGICORE', and for Altera devices, you can select 'ALTERAFPFUNCTIONS' or 'ALTFP'. Check library documentation for their supported devices.

Create a floating-point target configuration object for ALTERAFPFUNCTIONS.

fc = hdlcoder.createFloatingPointTargetConfig('ALTERAFPFUNCTIONS');

Set the configuration object on the model

hdlset_param('hdlcoderFocCurrentSingleTargetHdl', 'FloatingPointTargetConfiguration', fc);

To compile and simulate the generated code with QuestaSim, you have to compile Altera simulation library and set its path on model with the SimulationLibPath parameter. Check Tool Setup documentation for more information. alterasimulationlibpath returns the path to the compiled Altera simulation library in our environment.

hdlset_param('hdlcoderFocCurrentSingleTargetHdl', 'SimulationLibPath', alterasimulationlibpath);

Altera Megafunction (ALTERAFPFUNCTIONS) library allows generating IP modules for a given target frequency. In this example, the target frequency is set to 250MHz.

hdlset_param('hdlcoderFocCurrentSingleTargetHdl', 'TargetFrequency', 250);

Remodeling for IP mapping

Generate code

try
    makehdl('hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control');
catch me
    disp(me.message);
end

The error message indicates that the Dynamic Saturation block cannot map to floating-point library.

hilite_system('hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/DQ_Current_Control/D_Current_Control/Saturate_Output/Saturation_Dynamic');

The blocks supported for floating-point library mapping is a subset of all HDL Coder supported blocks. Saturation dynamic is an example of a block that is supported for fixed-point, but not floating-point mapping. In these cases, the block may be described as a sub-graph of the supported subset. For a complete list of blocks and modes that can map to floating-point libraries, check Block support for Floating-Point Library Mapping documentation.

In this example, we will replace the Saturate_Output subsystems that contains Dynamic Saturation blocks with an alternative implementation.

open_system('floatFocUtils');
blocksToReplace = {'hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/DQ_Current_Control/D_Current_Control/Saturate_Output', ...
    'hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/DQ_Current_Control/Q_Current_Control/Saturate_Output'...
    };
position1 = get_param(blocksToReplace{1}, 'Position');
delete_block(blocksToReplace{1});
add_block('floatFocUtils/Saturate_Output_Detailed', ...
    blocksToReplace{1}, 'Position', position1);
position2 = get_param(blocksToReplace{2}, 'Position');
delete_block(blocksToReplace{2});
add_block('floatFocUtils/Saturate_Output_Detailed', ...
    blocksToReplace{2}, 'Position', position2);
bdclose('floatFocUtils');
open_system(blocksToReplace{1}, 'force');

Applying clock rate pipelining to resolve IP latencies

Try to generate code again

try
    makehdl('hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control');
catch me
    disp(me.message);
end

These error messages indicate that HDL Coder cannot replace operations in feedback loops with floating-point IP modules, because those loops are modeled with fewer delays than the latency of the equivalent floating-point IP modules to be replaced with. Floating-point IP modules are implemented as pipelined blocks. For some modules, there are minimum latency requirements. As changing the latency of the feedback loop generates an incorrect implementation, HDL Coder prevents the addition of such latency inside feedback loop.

The error indicates that the adder inside the feedback loop requires multiple cycles but the loop has only one delay.

hilite_system('hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/DQ_Current_Control/D_Current_Control/Add');

There are a few options available in this situation:

  • Reduce the target frequency may lower the pipelining depth requirement. But, this may also slow down all other IP modules in the design.
  • Configure the IP modules used in the loop with a smaller latency. This also slows down the IP modules' operating frequency, but only for specified IP modules.
  • Apply clock rate pipelining. When the data rate is slower than the FPGA clock rate, FPGA has multiple cycles at clock rate to finish operations and still retains the numerical consistency. For more information about clock rate pipelining, see Clock-Rate Pipelining documentation.

Let us apply the clock-rate pipelining option to solve the feedback loop problem, since the sample time of 20 $\mu s$ and the FPGA target frequency of 250 MHz (or 4 ns). Thus, we define the Oversampling factor as the ratio of the two values, i.e. 5000, meaning that one unit delay, such as the one shown in the loop, with sample time of 20 $\mu s$ in the original model, is equivalent to 5000 clock rate cycles at sample time of 4 ns on the FPGA. They are sufficient for floating-point IP modules in the loops. Clock rate pipelining is an ideal option for this design.

Set oversampling to 5000.

hdlset_param('hdlcoderFocCurrentSingleTargetHdl', 'Oversampling', 5000);

Generate code

makehdl('hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control');

Now, the entire design is mapped to floating-point IP modules. The target code generation report summarizes the floating IP module usage.

Inspect the generated model for implementation details. For example, subsystem gm_hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/DQ_Current_Control/D_Current_Control/Add corresponding to hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/DQ_Current_Control/D_Current_Control/Add in the original model shows that this operation takes 3 cycles.

hilite_system('gm_hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/DQ_Current_Control/D_Current_Control/Add/Add_pd1');
get_param('gm_hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/DQ_Current_Control/D_Current_Control/Add/Add_pd1', ...
    'DelayLength')

Since floating-point IP modules introduce latencies across the design, HDL Coder automatically adds necessary matching delays to maintain data synchronization. See Delay Balancing documentation for more details on delay balancing.

Share Floating-point IPs

Floating-point IP modules are suitable to share, because they are usually identical for the same kind. Floating point IPs are typically expensive operations and it is desirable to share these resources, if possible, to reduce the area footprint. HDL Coder shares resources in the same subsystem. In order to allow more resources to share, we flatten the subsystem hierarchy and set resource sharing factor on the top network.

hdlset_param('hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control', 'FlattenHierarchy', 'on');
hdlset_param('hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control', 'SharingFactor', 4);

Generate code

makehdl('hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control');

We can confirm that fewer IP modules are inferred from the Floating-point resource report now.

We can also observe the resource sharing results by inspecting the generated model.

open_system('gm_hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control');
set_param('gm_hdlcoderFocCurrentSingleTargetHdl', 'SimulationCommand', 'update');
set_param('gm_hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control', 'ZoomFactor', 'FitSystem');
hilite_system('gm_hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/crp_temp_shared');
hilite_system('gm_hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/crp_temp_shared1');
hilite_system('gm_hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/crp_temp_shared2');
hilite_system('gm_hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/crp_temp_shared3');
hilite_system('gm_hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/crp_temp_shared4');
hilite_system('gm_hdlcoderFocCurrentSingleTargetHdl/FOC_Current_Control/crp_temp_shared5');

IP Library Configuration

Floating point IP libraries provide some customization options for their IP modules. In this section, we illustrate how to control this configuration in the HDL Coder workflow.

We will use XILINX LOGICORE and a simple model containing one add block for this section.

Create a floating-point target configuration object for XILINX LOGICORE.

fc = hdlcoder.createFloatingPointTargetConfig('XILINXLOGICORE');

In addition to library name, the configuration object has two other fields for library settings and individual IP module settings, respectively.

fc

LibrarySettings contains library-wide settings. Check the setting for XILINX LOGICORE library.

fc.LibrarySettings

These are the settings applicable to all IP modules from this library. For example Objective specifies the c_optimization parameter to XILINX LOGICORE. We can switch it to 'AREA'.

fc.LibrarySettings.Objective = 'AREA';
fc.LibrarySettings

Library settings are library specific. See FPGA Floating-Point Library Mapping documentation for all settings for specific libraries.

IPConfig provides settings, such as Latency and ExtraArgs, for individual IP modules.

As shown in the previous section, latency is a critical property for IP mapping. HDL Coder infers latency based on library settings and target frequency, if applicable. We can also specify latency for individual IP module with the configuration object and HDL Coder uses them for code generation and optimizations.

fc.IPConfig.customize('ADDSUB', 'SINGLE', 'Latency', 11);
fc.IPConfig

The latency for the ADDSUB IP becomes 11 instead of the default value 12.

Other IP specific settings are specified with ExtraArgs. For example, HDL Coder calls XILINX LOGICORE to generate floating-point IP modules without using any DSP blocks by default. XILINX LOGICORE provides a parameter c_mult_usage to control DSP usage. In order to use DSP blocks, we can pass a different setting with ExtraArgs to override the default behavior. Because the ExtraArgs string is appended to the default IP module generation parameters, it must comply with library setting syntax. Check IP library documents for parameter usage and syntax.

fc.IPConfig.customize('ADDSUB', 'SINGLE', 'ExtraArgs', 'CSET c_mult_usage=Full_Usage');
fc.IPConfig

Open the model and set the configuration object on it.

open_system('hdlcoder_targetIP_configuration');
hdlset_param('hdlcoder_targetIP_configuration', 'FloatingPointTargetConfiguration', fc);

Run synthesis and mapping to confirm the DSP block usage.

  hWC = hdlcoder.WorkflowConfig('SynthesisTool','Xilinx ISE', ...
      'TargetWorkflow','Generic ASIC/FPGA');
  hWC.SkipPreRouteTimingAnalysis = true;
  hWC.RunTaskAnnotateModelWithSynthesisResult = false;
  hWC.GenerateRTLCode = true;
  hWC.validate;
  hdlcoder.runWorkflow('hdlcoder_targetIP_configuration/Add_Subsystem', hWC);

Summary

HDL Coder bridges the gap between high level algorithms modeled with floating-point and low level FPGA implementation details. It not only automates the process for fast prototyping, but also allows you to explore high level algorithm design choices efficiently. This example demonstrates the necessary steps to generate synthesizable floating-point HDL code. The command-line APIs used in this example helps to automate your entire code generation process and design space exploration. All the APIs have corresponding GUI settings for ease of usage. For more information about the APIs and GUI options, check FPGA Floating-Point Library Mapping documentation.