Main Content

Generate Native Half-Precision C Code from Simulink Models

Some embedded hardware targets natively support special types for half precision, such as _Float16 and _fp16 data types for ARM® compilers. You can generate native half-precision C code for embedded hardware targets that natively support half precision floating-point data types. The process to generate native half C code is as follows:

  • Register a new hardware target device that natively supports half precision using the target namespace.

  • Set up the Simulink® model for native half code generation with the new target hardware.

  • Configure the Simulink model toolchain and set up the compiler for half precision.

  • Generate native half type code.

Fixed-Point Designer™ includes preconfigured language implementations for Armclang and GCC compilers. For other hardware targets, you can specify a custom language implementation based on your hardware specifications.

Generate Native Half-Precision C Code for ARM Cortex-A with Armclang Compiler

In this example, an ARM Cortex®-A processor is used as the hardware target. The model is configured to use this ARM target and the Armclang compiler toolchain. Refer to the ARM developer documentation for detailed information on using the half-precision data type on this hardware. See Arm Compiler armclang Reference Guide: Half-precision floating-point data types.

Register Target Hardware

Use the target.create function to create an ARM Cortex-A processor target that is compatible with half precision.

arm_half = target.create('Processor', ...
'Manufacturer', "ARM Compatible", ...
'Name', 'ARM Cortex-A Half-Precision');

Add the language implementation. The 32-bit version of Clang for ARM has half precision enabled by default. Use target.get to retrieve the target object from the internal database.

li = target.get('LanguageImplementation',"Clang ARM 32-bit");

Replace the default language implementation for ARM Cortex with Armclang.

arm_half.LanguageImplementations = li;

Use the target.add function to add the target object to the internal database.

target.add(arm_half);

Open Half-Precision Model

Open the model for which you want to generate native half-precision C code. The model should use half-precision data types and be configured for code generation.

model = 'simpleHalfModel';
open_system(model);

Set up Simulink Model with Target Hardware

Optionally, enable the model parameter Inherit floating-point output type smaller than single precision to propagate half precision.

set_param(model,'InheritOutputTypeSmallerThanSingle','on');

Set the code generation target to the ARM target modified for half precision.

set_param(model,'ProdEqTarget','on'); 
set_param(model,'ProdHWDeviceType',...
    'ARM Compatible->ARM Cortex-A Half-Precision');

Set up Simulink Model with Armclang Compiler Toolchain

Register the Armclang compiler toolchain according to Setup and Configure Armclang Compiler Toolchain for Code Generation.

Set up the Armclang compiler toolchain for code generation.

set_param(model,'SystemTargetFile','ert.tlc');
set_param(model,'GenCodeOnly','off');
set_param(model,'Toolchain','Armclang Compiler');
set_param(model,'BuildConfiguration','Specify');

Configure the Armclang compiler for custom C flags.

compilerOptions = get_param(model,'CustomToolchainOptions');
compilerIdx = find(strcmp(compilerOptions,'C Compiler'));

Add half-precision flags.

appendToCCompilerOptions = ' --target=arm-arm-none-eabi -mcpu=cortex-a75+fp16';
compilerOptions{compilerIdx+1} = strcat(compilerOptions{compilerIdx+1},appendToCCompilerOptions);

Apply the custom toolchain options to the model.

set_param(model,'CustomToolchainOptions',compilerOptions );

You can confirm these settings in the Code Generation pane of the Configuration Parameters dialog box.

Code Generation pane after setting up custom Armclang compiler.

Generate Code

Once the model has been set up for the ARM Cortex-A target hardware and the Armclang compiler toolchain, you can generate C code. You can only run the model executable on an ARM target or emulator.

Use the slbuild function to build a standalone executable for the model.

slbuild(model);

You can inspect the code generation report to confirm that the custom header and type definitions are used.

Screenshot of generated header file.

The generated code uses the native half-precision data type.

Screenshot of generated C code showing real16_T is used.

Register ARM Cortex-A with GCC Compiler

In this example, an ARM Cortex-A processor is used as the hardware target. The model is configured to use this ARM target and the GCC compiler toolchain.

Use the target.create function to create an ARM Cortex-A processor target.

arm_half = target.create('Processor', ...
'Manufacturer', "ARM Compatible", ...
'Name', 'ARM Cortex-A Half-Precision');

Add the language implementation. The 32-bit version of GCC for ARM has half precision enabled by default. Use the target.get function to retrieve the target object from the internal database.

li = target.get('LanguageImplementation',"GNU GCC ARM 32-bit");

Replace the default language implementation for ARM Cortex with Armclang.

arm_half.LanguageImplementations = li;

Use the target.add function to add the target object to the internal database.

target.add(arm_half);

Register ARM Target Hardware with Custom Language Implementation

In this example, you create a new custom language implementation with half precision for a compatible ARM target.

Use the target.create function to copy the ARM Compatible-ARM Cortex language implementation.

languageImplementation = target.create('LanguageImplementation',...
    'Name','ARM with half',...
    'Copy','ARM Compatible-ARM Cortex');

Specify custom half information and target specific headers, as given by your target hardware documentation. For more information, see Register New Hardware Devices.

customHalf = target.create('FloatingPointDataType',...
    'Name','BCM2711 Half Type',...
    'TypeName','float16_T',...
    'LiteralSuffix','f16',...
    'Size',16, ...
    'SystemIncludes',"arm_fp16.h,arm_neon.h");
languageImplementation.DataTypes.NonStandardDataTypes = customHalf;

Provide information about your target processor.

% Broadcom BCM2711
% Quad core Cortex-A72 (ARM v8) 64-bit SoC
pi4a72 = target.create('Processor',...
    'Manufacturer','Broadcom',...
    'Name','BCM2711');

Add the custom half precision language implementation.

pi4a72.LanguageImplementations = languageImplementation;

Use the target.add function to add the target object to the internal database.

target.add(pi4a72);

Register Other Hardware Targets for Half Precision

This example uses a Renesas® RH850 to show you how to set up non-ARM targets for half precision.

Use the target.create function to create the target processor.

prh850 = target.create('Processor', ...
'Manufacturer', 'Renesas', ...
'Name', 'Renesas-RH850 With Half', ...
'Copy', 'Renesas-RH850');

Add the language implementation.

li = target.create('LanguageImplementation',...
'Name', 'Renesas-RH850 with Half', ...
'Copy', 'Renesas-RH850');

Provide additional information the custom half-precision implementation for this hardware.

customHalf = target.create('FloatingPointDataType',...
    'Name','Renesas Half Type');
customHalf.TypeName = '__fp16';
customHalf.Size = 16;
customHalf.LiteralSuffix = '';
customHalf.SystemIncludes = '';

Add the custom half-precision data type.

li.DataTypes.NonStandardDataTypes = customHalf;
prh850.LanguageImplementations = li;

Use the target.add function to add the target object to the internal database.

target.add(prh850);

See Also

| | | |

Related Topics

External Websites