Integrating GPU Coder™ into Simulink®

This example shows how to integrate GPU Coder™ into Simulink®. GPU Coder is not supported for Simulink blocks, but you can still leverage GPUs in Simulink by generating a dynamic linked library (dll) using GPU Coder, and then integrating it into a Simulink block by using coder.ExternalDependency APIs. Sobel edge detection is used as an example to demonstrate this concept.

Prerequisites

  • CUDA enabled NVIDIA® GPU with compute capability 3.2 or higher.

  • NVIDIA CUDA toolkit and driver.

  • Simulink to create the model in Simulink.

  • Computer Vision Toolbox™ to use the video reader and viewer used in the example.

  • Environment variables for the compilers and libraries. For information on the supported versions of the compilers and libraries, see Third-party Products. For setting up the environment variables, see Setting Up the Prerequisite Products.

Verify GPU Environment

To verify that the compilers and libraries necessary for running this example are set up correctly, use the coder.checkGpuInstall function.

envCfg = coder.gpuEnvConfig('host');
envCfg.BasicCodegen = 1;
envCfg.Quiet = 1;
coder.checkGpuInstall(envCfg);

The Sobel Edge Detection Entry-Point Function

The sobelEdge.m function takes an image (represented as a single matrix) and returns an image with the edges detected.

type sobelEdge
function [ magnitude ] = sobelEdge( Image )
%#codegen

%   Copyright 2017-2019 The MathWorks, Inc.


maskX = single([-1 0 1 ; -2 0 2; -1 0 1]);
maskY = single([-1 -2 -1 ; 0 0 0 ; 1 2 1]);

coder.gpu.kernelfun();



resX = conv2(Image, maskX, 'same');
resY = conv2(Image, maskY, 'same');

magnitude = sqrt(resX.^2 + resY.^2);
thresh = magnitude < 0.4;
magnitude(thresh) = 0;

end

Generate a Dynamic Library for the Function

To run this function on the GPU from Simulink, generate a shared library by using GPU Coder. Call the generated code (library) from Simulink by using coder.ExternalDependency APIs. Copy the generated library to top level folder.

Isize = single(zeros(240, 320));
cfg = coder.gpuConfig('dll');
codegen -args {Isize} -config cfg sobelEdge
if ispc
    copyfile(fullfile(pwd, 'codegen','dll', 'sobelEdge','sobelEdge.dll'), pwd);
else
    copyfile(fullfile(pwd, 'codegen','dll', 'sobelEdge','sobelEdge.so'), pwd);
end

Before generating CUDA code, first test the MEX function in MATLAB® to ensure that it is functionally equivalent to the original MATLAB code and that no run-time errors occur.

Define coder.ExternalDependency API to Invoke the Generated Code

The SobelAPI.m is a class that defines the API to invoke the generated dynamic library. Most of this function is a standard template. The method of interest is SobelAPI.sobelEdge, which is called to execute the dynamic library. This function invokes the sobelEdge dynamic library through a coder.ceval call.

type SobelAPI

%   Copyright 2017-2019 The MathWorks, Inc.

classdef SobelAPI < coder.ExternalDependency
    %#codegen
    
    methods (Static)
        
        function bName = getDescriptiveName(~)
            bName = 'SobelAPI';
        end
        
        function tf = isSupportedContext(ctx)
            if  ctx.isMatlabHostTarget()
                tf = true;
            else
                error('Sobel library not available for this target');
            end
        end
        
        function updateBuildInfo(buildInfo, ctx)
            [~, linkLibExt, execLibExt, ~] = ctx.getStdLibInfo();
            
            % Header files.
            hdrFilePath = fullfile(pwd, 'codegen', 'dll', 'sobelEdge');
            buildInfo.addIncludePaths(hdrFilePath);
            
            % Link files.
            linkFiles = strcat('sobelEdge', linkLibExt);
            linkPath = hdrFilePath;
            linkPriority = '';
            linkPrecompiled = true;
            linkLinkOnly = true;
            group = '';
            buildInfo.addLinkObjects(linkFiles, linkPath, ...
                linkPriority, linkPrecompiled, linkLinkOnly, group);
            
            % Non-build files.
            nbFiles = 'sobelEdge';
            nbFiles = strcat(nbFiles, execLibExt);
            buildInfo.addNonBuildFiles(nbFiles,'','');
        end
        
        %API for library function 'sobelEdge'.
        function c = sobelEdge(I)
            % Running in generated code, call library function.
            coder.cinclude('sobelEdge.h');
            
            c = coder.nullcopy(I);
            coder.ceval('sobelEdge', coder.rref(I), coder.wref(c));
        end
    end
end

Create a Simulink Model That Integrates the API to the DLL

In Simulink, create a MATLAB function block that calls SobelAPI.sobelEdge. This step is equivalent to executing the GPU Coder generated DLL code. When the MATLAB function block executes, this dynamic library runs on your host machine's GPU. And similarly for code-generation from Simulink, the CUDA code is invoked. The Simulink model uses a video reader and a video display to show the effect of the algorithm.

open_system('gpucoder_sobelEdge');
set_param('gpucoder_sobelEdge', 'SimulationCommand', 'update');

Run the Simulink Model (The Sobel Filter)

Run simulation to see the effect of the Sobel algorithm.

sim('gpucoder_sobelEdge', 'timeout', 30);