Generate DPI Component Using MATLAB

Create MATLAB Function and Test Bench

Create MATLAB Function

Code the MATLAB® function you want to export to a SystemVerilog environment. For information about coding MATLAB functions, see "Function Basics" in the MATLAB documentation.

Consider adding the compilation directive %#codegen to your function. This directive can help you diagnose and fix violations that would result in errors during code generation. See Compilation Directive %#codegen (MATLAB Coder).

While you code your function, keep in mind the Limitations, which describe the various aspects of DPI component generation that you must know. These aspects include which data types are valid, what files are generated, and how the shared libraries are compiled.

In this example, the MATLAB function fun.m takes a single input and multiplies it by 2. The function includes the compilation directive %#codegen.

function y = fun(x);
%#codegen
y = x * 2;

The process of creating the MATLAB includes writing the code, creating the test bench, and running the test bench in an iterative process. When you are satisfied that your function does what you intend it to do, continue on to Generate SystemVerilog DPI Component.

Create Test Bench

Create a test bench to exercise the function. In this example, the test bench applies a test vector against fun.m and plots the output.

function sample=fun_tb
% Testbench should not require input, however you can give an output.

% Define a test vector 
tVecIn = [1,2,3,4,5];

% Exercise fun.m and plot results to make sure function is working correctly
tVecOut = arrayfun(@(in) fun(in),tVecIn);
plot(tVecIn,tVecOut);
grid on;

% Get my sample input to use it with function dpigen.
sample = tVecIn(1);

Note that a test bench should not have inputs. The test bench can load test vectors using MAT files or any other data file, so it does not require inputs.

The output of fun_tb, sample, is going to be used as the function inputs argument for fun.m during the call to dpigen, which is why it is a single element. See Generate SystemVerilog DPI Component.

Run Test Bench

fun_tb
ans =

     1

Next, generate the SystemVerilog DPI component. See Generate SystemVerilog DPI Component.

Generate SystemVerilog DPI Component

Generate DPI Component with dpigen Function

Use the function dpigen to generate the DPI component. This function has several optional input arguments. At a minimum, specify the MATLAB function you want to generate a component for and the function inputs. If you also want to generate a test bench to exercise the generated component, use the -testbench option.

dpigen func -args input_arg -testbench test_bench_name 
  1. Define the inputs as required by the function. In this example, sample is a scalar value of type double.

    sample = 1;
  2. Call the DPI component generator function:

    dpigen fun -args sample -testbench fun_tb 

    The command, issued as shown, performs the following tasks:

    • Generates fun_dpi.sv – a SystemVerilog component for the function fun.m.The function inputs for fun.m are specified in sample.

    • Generates fun_dpi_pkg.sv – a SystemVerilog package file. This file contains all the imported function declarations.

    • Creates a test bench for the generated component.

    For this call to dpigen, MATLAB outputs the following messages:

    ### Generating DPI Wrapper fun_dpi.c
    ### Generating DPI Wrapper header file fun_dpi.h
    ### Generating SystemVerilog module package fun_dpi_pkg.sv
    ### Generating SystemVerilog module fun_dpi.sv
    ### Generating makefiles for: fun_dpi
    ### Compiling the DPI Component
    ### Generating SystemVerilog test bench fun_tb.sv
    ### Generating test bench simulation script for Mentor Graphics QuestaSim/Modelsim run_tb_mq.do
    ### Generating test bench simulation script for Cadence Incisive run_tb_incisive.sh
    ### Generating test bench simulation script for Cadence Xcelium run_tb_xcelium.sh
    ### Generating test bench simulation script for Synopsys VCS run_tb_vcs.sh
    ### Generating test bench simulation script for Vivado Simulator run_tb_vivado.bat
    

    The function shown in the previous example generates the following folders and files:

Examine Generated Package File

Examine the generated package file. Note the declarations of the initialize, reset, terminate, and fun functions.

This example shows the code generated for fun_dpi_pkg.sv.

// File: C:\fun_example\codegen\dll\fun\fun_dpi_pkg.sv
// Created: 2017-12-19 09:18:00
// Generated by MATLAB 9.5 and HDL Verifier 5.4

`timescale 1ns / 1ns
package fun_dpi_pkg;

// Declare imported C functions
import "DPI" function chandle DPI_fun_initialize(input chandle existhandle);
import "DPI" function chandle DPI_fun_reset(input chandle objhandle,input real x,output real y);
import "DPI" function void DPI_fun(input chandle objhandle,input real x,output real y);


import "DPI" function void DPI_fun_terminate(input chandle existhandle);

endpackage : fun_dpi_pkg

Examine Generated Component

Examine the generated component so that you can understand how the dpigen function converted MATLAB code to SystemVerilog code. For more information on what the function includes, see Generated SystemVerilog Wrapper.

This example shows the code generated for fun_dpi.sv.

// File: C:\fun_example\codegen\dll\fun\fun_dpi.sv
// Created: 2017-12-19 09:18:00
// Generated by MATLAB 9.5 and HDL Verifier 5.4

`timescale 1ns / 1ns

import fun_dpi_pkg::*;

module fun_dpi(
    input bit clk,
    input bit clk_enable,
    input bit reset,
    input real x,
    output real y
);

    chandle objhandle=null;
    real y_temp;
    
    initial begin
        objhandle=DPI_fun_initialize(objhandle);
    end

    final begin
        DPI_fun_terminate(objhandle);
    end

    always @(posedge clk or posedge reset) begin
        if(reset== 1'b1) begin
            objhandle=DPI_fun_reset(objhandle,x,y_temp);
            y<=y_temp;
        end
        else if(clk_enable) begin
            DPI_fun(objhandle,x,y_temp);
            y<=y_temp;
        end
    end
endmodule

Examine Generated Test Bench

Examine the generated test bench so you can see how function dpigen created this test bench from the MATLAB code. For more information on the generated test bench, see Generated Test Bench.

This example shows the code generated for fun_tb.sv.

// File: C:\fun_example\codegen\dll\fun\dpi_tb\fun_tb.sv
// Created: 2017-12-19 09:18:13
// Generated by MATLAB 9.5 and HDL Verifier 5.4

`timescale 1ns / 1ns
module fun_tb;
    real x;
    real y_ref;
    real y_read;
    real y;
    // File Handles
    integer fid_x;
    integer fid_y;
    // Other test bench variables
    bit clk;
    bit clk_enable;
    bit reset;
    integer fscanf_status;
    reg testFailure;
    reg tbDone;
    bit[63:0] real_bit64;
    bit[31:0] shortreal_bit64;
    parameter CLOCK_PERIOD= 10;
    parameter CLOCK_HOLD= 2;
    parameter RESET_LEN= 2*CLOCK_PERIOD+CLOCK_HOLD;
    // Initialize variables
    initial begin
        clk = 1;
        clk_enable = 0;
        testFailure = 0;
        tbDone = 0;
        reset = 1;
        fid_x = $fopen("dpig_in1.dat","r");
        fid_y = $fopen("dpig_out1.dat","r");
        // Initialize multirate counters
        #RESET_LEN reset = 0;
    end
    // Clock
    always #(CLOCK_PERIOD/2) clk = ~ clk;
    always@(posedge clk) begin
        if (reset == 0) begin
            #CLOCK_HOLD
            clk_enable <= 1;
            fscanf_status = $fscanf(fid_x, "%h", real_bit64);
            x = $bitstoreal(real_bit64);
            if ($feof(fid_x)) 
                tbDone = 1;
            fscanf_status = $fscanf(fid_y, "%h", real_bit64);
            y_read = $bitstoreal(real_bit64);
            if ($feof(fid_y)) 
                tbDone = 1;
            y_ref <= y_read;
            if (clk_enable == 1) begin
                assert ( ((y_ref - y) < 2.22045e-16) && ((y_ref - y) > -2.22045e-16) ) else begin
                    testFailure = 1;
                    $display("ERROR in output y_ref at time %0t :", $time);
                    $display("Expected %e; Actual %e; Difference %e", y_ref, y, y_ref-y);
                end
                if (tbDone == 1) begin
                    if (testFailure == 0) 
                        $display("**************TEST COMPLETED (PASSED)**************");
                    else
                        $display("**************TEST COMPLETED (FAILED)**************");
                    $finish;
                end
            end
        end
    end

    // Instantiate DUT
    fun_dpi u_fun_dpi(
    .clk(clk),
    .clk_enable(clk_enable),
    .reset(reset),
    .x(x),
    .y(y)
    );
endmodule

Next, run the generated test bench in the HDL simulator. See Run Generated Test Bench in HDL Simulator. If you plan to port the component and optional test bench from Windows® to Linux®, see Port Generated Component and Test Bench to Linux.

Run Generated Test Bench in HDL Simulator

This section includes instructions for running the generated test bench in one of the supported HDL simulators: Mentor Graphics® ModelSim® and Questa®Sim, Cadence Incisive®, and Synopsys® VCS®. It is possible that this code will work in other (unsupported) HDL simulators but it is not guaranteed.

Choose the workflow for your HDL simulator.

Run Test Bench in ModelSim and QuestaSim Simulators

  1. Start ModelSim or QuestaSim in GUI mode.

  2. Change your current directory to the dpi_tb folder under the code generation directory in MATLAB.

  3. Enter the following command in the shell to start your simulation:

    do run_tb_mq.do

    This generated script contains the name of the component and test bench, and instructions to the HDL simulator for running the test bench.

    When the simulation finishes, you should see the following text displayed in your console:

    **************TEST COMPLETED (PASSED)**************

    This message tells you that the test bench was run against the generated component successfully.

The following wave form image from this example demonstrates that the generated test bench was successfully exercised in the HDL simulator.

Next, import your component. See Use Generated DPI Functions in SystemVerilog.

Run Test Bench in Incisive Simulator

  1. Launch Incisive®.

  2. Start your terminal shell.

  3. Change the current directory to dpi_tb under the code generation directory in MATLAB.

  4. Enter the following command in the shell to start the simulation:

    sh run_tb_ncsim.sh

    This generated script contains the name of the component and test bench, and instructions to the HDL simulator for running the test bench.

    When the simulation finishes, you should see the following text displayed in your console:

    **************TEST COMPLETED (PASSED)**************

    This message tells you that the test bench was run against the generated component successfully.

Run Test Bench in VCS Simulator

  1. Launch VCS.

  2. Start your terminal shell.

  3. Change the current directory to dpi_tb under the code generation directory in MATLAB.

  4. Enter the following command in your shell to start the simulation:

    sh run_tb_vcs.sh

    This generated script contains the name of the component and test bench, and instructions to the HDL simulator for running the test bench.

    When the simulation finishes, you should see the following text displayed in your console:

    **************TEST COMPLETED (PASSED)**************

    This message tells you that the test bench was run against the generated component successfully.

Use Generated DPI Functions in SystemVerilog

To use the generated DPI component in a SystemVerilog test bench, first you must include the package file in your SystemVerilog environment. This will have the DPI functions available within the scope of your SystemVerilog module. Then, you must call the generated functions. When you compile the SystemVerilog code that contains the imported generated functions, use a DPI-aware SystemVerilog compiler and specify the component and package file names along with the SystemVerilog code.

The following example demonstrates adding the generated DPI component for fun.m to a SystemVerilog module.

  1. Call the Initialize function.

    DPI_fun_initialize();
  2. Call the function generated from fun.m.

    DPI_fun(x,y);

You can now modify the generated code as needed.

Example

module test_twofun_tb;

	initial begin
		DPI_fun_initialize();
	end

	always@(posedge clk) begin
		#1
		DPI_fun(x,y);
	end

Port Generated Component and Test Bench to Linux

To port the component and optional test bench from a Windows operating system to a Linux operating system, follow these instructions.

Note

You must have an Embedded Coder® license for the Windows to Linux port. Although it is possible the port will work without the Embedded Coder license, that usage is not supported.

Step 1. Tasks on the Windows Host Machine

  1. Create a MATLAB Coder™ config object. Change the target HW device type to LP64 for the Linux operating system.

    cfg=coder.config('dll');
    
    cfg.HardwareImplementation.TargetHWDeviceType='Generic->64-bit Embedded Processor (LP64)';
    
  2. Run the dpigen command using option -config to use the config object that you created in step 1. Use option -c so that function dpigen generates only code. For example:

    dpigen -config cfg DataTypes.m -args InputSample -c
  3. To generate a zip file to port to Linux, change folder to the source folder (where the buildInfo.mat file is generated), and execute the following commands at the command prompt:

    load buildInfo
    buildInfo.packNGo()
  4. To copy the file for porting, return to the top level folder. Find the zip file generated in the previous step (same name as the MATLAB function). Copy the zip file to the Linux machine.

Step 2. Tasks on the Linux Target Machine

  1. Unzip the file using the -j option to extract all the files with a flattened folder structure. You can unzip into any folder. For example:

    unzip -j  DataTypes.zip
    1. Copy this generic makefile script into an empty file:

      SRC=$(wildcard *.c)
      OBJ=$(SRC:.c=.o)
      
      SHARE_LIB_NAME=DPI_Component.so
      
      all: $(SRC) $(SHARE_LIB_NAME)
      	@echo "### Successfully generated all binary outputs."
      
      $(SHARE_LIB_NAME): $(OBJ)
      	gcc -shared -lm $(OBJ) -o $@
      
      .c.o:
      	gcc -c -fPIC -Wall -pedantic -Wno-long-long -fwrapv -O0 $< -o $@
      
    2. Replace DPI_Component.so with the name of the shared library you want to create.

    3. Save the script as Porting_DPIC.mk in the folder where the zip files were extracted.

  2. Build the shared library with the following command:

    make -f Porting_DPIC.mk all

    To use the generated component with SystemVerilog, see Use Generated DPI Functions in SystemVerilog.

  3. (Optional) Run the test bench that was automatically generated in Windows.

    1. Copy the contents of the dpi_tb folder from the Windows host machine to the Linux target machine.

    2. Run the test bench.

    To run the test bench in an HDL simulator, see Run Generated Test Bench in HDL Simulator.