Main Content

Integrate Custom HDL Code Into MATLAB Design

hdl.BlackBox provides a way to include custom HDL code, such as legacy or handwritten HDL code, in a MATLAB® design intended for HDL code generation.

When you create a user-defined System object™ that inherits from hdl.BlackBox, you specify a port interface and simulation behavior that matches your custom HDL code.

HDL Coder™ simulates the design in MATLAB using the behavior you define in the System object. During code generation, instead of generating code for the simulation behavior, the coder instantiates a module with the port interface you specify in the System object.

To use the generated HDL code in a larger system, you include the custom HDL source files with the rest of the generated code.

Define the hdl.BlackBox System object

  1. Create a user-defined System object that inherits from hdl.BlackBox.

  2. Configure the black box interface to match the port interface for your custom HDL code by setting hdl.BlackBox properties in the System object.

  3. Define the step method such that its simulation behavior matches the custom HDL code.

    Alternatively, the System object you define can inherit from hdl.BlackBox class, and you can define output and update methods to match the custom HDL code simulation behavior.

Example Code

For example, the following code defines a System object, CounterBbox, that inherits from hdl.BlackBox and represents custom HDL code for a counter that increments until it reaches a threshold. The CounterBbox reset and step methods model the custom HDL code behavior.

classdef CounterBbox < hdl.BlackBox % derive from hdl.BlackBox class
    %Counter: Count up to a threshold.
    % This is an example of a discrete-time System object with state
    % variables.
    properties (Nontunable)
        Threshold = 1
    properties (DiscreteState)
        % Define discrete-time states.
        function obj = CounterBbox(varargin)
            % Support name-value pair arguments
            obj.NumInputs = 1;   % define number of inputs
            obj.NumOutputs = 1;  % define number of inputs
    methods (Access=protected)
        % Define simulation behavior.
        % For code generation, the coder uses your custom HDL code instead.
        function resetImpl(obj)
            % Specify initial values for DiscreteState properties
            obj.Count = 0;
        function myout = stepImpl(obj, myin)
            % Implement algorithm. Calculate y as a function of
            % input u and state.
            if (myin > obj.Threshold)
                obj.Count = obj.Count + 1;
            myout = obj.Count;

Use System object In MATLAB Design Function

After you define your System object, use it in the MATLAB design function by creating an instance and calling its step method.

To generate code, you also need to create a test bench function that exercises the top-level design function.

Example Code

The following example code shows a top-level design function that creates an instance of the CounterBbox and calls its step method.

function [y1, y2] = topLevelDesign(u)

persistent mybboxObj myramObj
if isempty(mybboxObj)
    mybboxObj = CounterBbox; % instantiate the black box
    myramObj = hdl.RAM('RAMType', 'Dual port');

y1 = step(mybboxObj, u); % call the system object step method
[~, y2] = step(myramObj, uint8(10), uint8(0), true, uint8(20));

The following example code shows a test bench function for the topLevelDesign function.

clear topLevelDesign
y1 = zeros(1,200);
y2 = zeros(1,200);
for ii=1:200
    [y1(ii), y2(ii)] = topLevelDesign(ii);
plot([1:200], y2)

Generate HDL Code

Generate HDL code using the design function and test bench code.

When you use the generated HDL code, include your custom HDL code with the generated HDL files.

Example Code

In the following generated VHDL® code for the CounterBbox example, you can see that the CounterBbox instance in the MATLAB code maps to an HDL component definition and instantiation, but HDL code is not generated for the step method.

USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
  PORT( clk              :   IN    std_logic;
        reset            :   IN    std_logic;
        clk_enable       :   IN    std_logic;
        u                :   IN    std_logic_vector(7 DOWNTO 0);  -- uint8
        ce_out           :   OUT   std_logic;
        y1               :   OUT   real;  -- double
        y2               :   OUT   std_logic_vector(7 DOWNTO 0)  -- uint8
END foo;
  -- Component Declarations
  COMPONENT CounterBbox
    PORT( clk             :   IN    std_logic;
          clk_enable      :   IN    std_logic;
          reset           :   IN    std_logic;
          myin            :   IN    std_logic_vector(7 DOWNTO 0);  -- uint8
          myout           :   OUT   real  -- double
    PORT( clk             :   IN    std_logic;
          enb             :   IN    std_logic;
          wr_din          :   IN    std_logic_vector(7 DOWNTO 0);  -- uint8
          wr_addr         :   IN    std_logic_vector(7 DOWNTO 0);  -- uint8
          wr_en           :   IN    std_logic;
          rd_addr         :   IN    std_logic_vector(7 DOWNTO 0);  -- uint8
          wr_dout         :   OUT   std_logic_vector(7 DOWNTO 0);  -- uint8
          rd_dout         :   OUT   std_logic_vector(7 DOWNTO 0)  -- uint8
  -- Component Configuration Statements
  FOR ALL : CounterBbox
    USE ENTITY work.CounterBbox(rtl);
  FOR ALL : DualPortRAM_Inst0
    USE ENTITY work.DualPortRAM_Inst0(rtl);
  -- Signals
  SIGNAL enb               : std_logic;
  SIGNAL varargout_1       : real := 0.0;  -- double
  SIGNAL tmp               : unsigned(7 DOWNTO 0);  -- uint8
  SIGNAL tmp_1             : unsigned(7 DOWNTO 0);  -- uint8
  SIGNAL tmp_2             : std_logic;
  SIGNAL tmp_3             : unsigned(7 DOWNTO 0);  -- uint8
  SIGNAL varargout_1_1     : std_logic_vector(7 DOWNTO 0);  -- ufix8
  SIGNAL varargout_2       : std_logic_vector(7 DOWNTO 0);  -- ufix8
  u_CounterBbox : CounterBbox
    PORT MAP( clk => clk,
              clk_enable => enb,
              reset => reset,
              myin => u,  -- uint8
              myout => varargout_1  -- double
  u_DualPortRAM_Inst0 : DualPortRAM_Inst0
    PORT MAP( clk => clk,
              enb => enb,
              wr_din => std_logic_vector(tmp),  -- uint8
              wr_addr => std_logic_vector(tmp_1),  -- uint8
              wr_en => tmp_2,
              rd_addr => std_logic_vector(tmp_3),  -- uint8
              wr_dout => varargout_1_1,  -- uint8
              rd_dout => varargout_2  -- uint8
  enb <= clk_enable;
  y1 <= varargout_1;
  --y2 = u;
  tmp <= to_unsigned(2#00001010#, 8);
  tmp_1 <= to_unsigned(2#00000000#, 8);
  tmp_2 <= '1';
  tmp_3 <= to_unsigned(2#00010100#, 8);
  ce_out <= clk_enable;
  y2 <= varargout_2;
END rtl;

Limitations for hdl.BlackBox

You cannot use hdl.BlackBox to assign values to a VHDL generic, or Verilog® or SystemVerilog parameter in your custom HDL code.

See Also

Related Topics