Hauptinhalt

Set Up PIL Connectivity by Using Target Framework

R2026b

Before you can run processor-in-the-loop (PIL) simulations, you must set up connectivity between Simulink® and your target hardware. The connectivity enables the PIL simulation to:

  • Build the target application.

  • Download, start, and stop the application on the target hardware.

  • Support communication between Simulink and the target hardware.

Note

For Polyspace® Test™ C/C++ testing on target hardware, the same steps are required to set up a connection between Polyspace Test and the target hardware.

Use one of these workflows.

WorkflowWhen to use
target namespace with rtiostream API

Use this workflow if your target application has large I/O requirements that need high-bandwidth communication. See Use rtiostream API for PIL Target Connectivity.

You can specify a debugger. See Support PIL Debugging and DebugExecutionTool Template.

target namespace with debugger

Use this workflow if:

  • You want a single debugger integration to provide PIL connectivity for target hardware systems that are supported by the debugger. This workflow requires little or no target-specific code.

  • The I/O requirements of the target application are small. For example, unit tests on a component. If you use this workflow for a target application that has large I/O requirements, the execution of the application is slower.

See Use Debugger for PIL Target Connectivity and DebugIOTool Template.

Use rtiostream API for PIL Target Connectivity

To provide PIL connectivity between Simulink and the target hardware, you can use the target namespace with the rtiostream API. In this example, your development computer is the target hardware.

  1. Create a target.Board object that provides MATLAB® with a description of your target hardware.

    hostTarget = target.create('Board', ...
                               'Name', 'Example Intel Board');

    To support code generation and data I/O in PIL simulations, associate the object with processor, connection, and communication descriptions.

  2. To support code generation, associate the board with a target.Processor object that contains a language implementation. For this example, create a target.Processor object and reuse an existing target.LanguageImplementation object. For information about setting up a custom target.LanguageImplementation object, see Register New Hardware Devices.

    processor = target.create('Processor', 'Name', 'ExampleProcessor');
    processor.LanguageImplementations = target.get('LanguageImplementation', ...
                                                   'Intel-x86-64 (Windows64)'); 

    Associate the target.Board object with the new target.Processor object by using the Processors property

    hostTarget.Processors = processor;

  3. Create an object that contains details about executing the target application. The object describes the tool that is required to run the target application on the target hardware. To capture system commands for starting and stopping the target application, you can use the target.HostProcessExecutionTool or target.SystemCommandExecutionTool class.

    applicationExecution = target.create('HostProcessExecutionTool');
    applicationExecution.Name = 'Windows Application';

    Create a target.Command object for downloading and running the target application. Assign the string variable '$(EXE)' to the String property as a place holder for the target application name, which is not known until execution.

    runCommand = target.create('Command');
    runCommand.String = '$(EXE)';
    applicationExecution.StartCommand = runCommand;
    hostTarget.Tools.ExecutionTools = applicationExecution;
  4. Create a target.CommunicationInterface object that provides the target hardware with details of the communication channel and the rtiostream API implementation.

    Use:

    • The shipped TCP/IP rtiostream implementation source file.

    • A target.BuildDependencies object to specify, for the rtiostream API, the source files that are compiled with the target application.

    • A target.MainFunction object to pass arguments to the target application

    comms = target.create('CommunicationInterface');
    comms.Name = 'Windows TCP Interface';
    comms.Channel = 'TCPChannel';
    comms.APIImplementations = target.create('APIImplementation', ...
                                             'Name', 'x86 RTIOStream Implementation');
    comms.APIImplementations.API = target.create('API', 'Name', 'RTIO Stream');
    comms.APIImplementations.BuildDependencies = target.create('BuildDependencies');
    comms.APIImplementations.BuildDependencies.SourceFiles = ...
                                               {fullfile('$(MATLAB_ROOT)', ...
                                                         'toolbox', ...
                                                         'coder', ...
                                                         'rtiostream', ...
                                                         'src', ...
                                                         'rtiostreamtcpip', ...
                                                         'rtiostream_tcpip.c')};
    comms.APIImplementations.MainFunction = target.create('MainFunction', ...
                                                          'Name', 'TCP RtIOStream Main');
    comms.APIImplementations.MainFunction.Arguments = {'-blocking', '1', '-port', '0'};
    hostTarget.CommunicationInterfaces = comms;
  5. This step is optional. A PIL simulation uses a communication protocol for the transfer of data between Simulink and the target hardware. The target.PILProtocol class describes the parameters of the protocol. For this example, you can improve target run-time performance by increasing the I/O buffer size that the protocol uses.

    Create a target.PILProtocol object and specify I/O buffer size.

    pilProtocol = target.create('PILProtocol');
    pilProtocol.Name = 'Windows PIL Protocol';
    pilProtocol.SendBufferSize = 50000;
    pilProtocol.ReceiveBufferSize = 50000;
    hostTarget.CommunicationProtocolStacks = pilProtocol;

    If you do not perform this step, default target.PILProtocol values are used.

  6. You can configure a PIL simulation to produce execution-time profiles for the generated code. To support code execution profiling, you must create a timer object that describes the retrieval of current time from generated code running on the target hardware. The timer object description must include a description of the function that retrieves time and its implementation.

    This example uses a C function, timestamp_x86, which returns the current time as a uint64 data type.

    timerSignature = target.create('Function');
    timerSignature.Name = 'timestamp_x86';
    timerSignature.ReturnType = 'uint64';

    Capture the function in an API object.

    timerApi = target.create('API');
    timerApi.Functions = timerSignature;
    timerApi.Language = target.Language.C;
    timerApi.Name = 'Windows Timer API';

    Capture the dependencies of the function, that is, the source and header files that are required to run the function.

    timerDependencies = target.create('BuildDependencies');
    timerDependencies.IncludeFiles = {'host_timer_x86.h'};
    timerDependencies.IncludePaths = {'$(MATLAB_ROOT)/toolbox/coder/profile/src'};
    timerDependencies.SourceFiles = {'host_timer_x86.c'};

    Create an object that combines the API and dependencies.

    timerImplementation = target.create('APIImplementation');
    timerImplementation.API = timerApi;
    timerImplementation.BuildDependencies = timerDependencies;
    timerImplementation.Name = 'Windows Timer Implementation';

    Create the timer object and associate it with the timer information.

    timer = target.create('Timer');
    timer.APIImplementation = timerImplementation; 
    timer.Name = 'Windows Timer';

    Assign the timer to the processor object.

    processor.Timers = timer;
  7. The previous steps created target hardware support for communications and running the target application. Now, set up a connection between your development computer and the target hardware by creating a target.TargetConnection object. Specify:

    • The communication channel, which is the same channel specified in the target hardware communication interface — see step 4.

    • The connection properties.

    • The target, which is the board description specified in previous steps.

    connection = target.create('TargetConnection');
    connection.Name = 'Host Process Connection';
    connection.Target = hostTarget;
    connection.CommunicationChannel = target.create('TCPChannel');
    connection.CommunicationChannel.Name = 'External Process TCPCommunicationChannel';
    connection.CommunicationChannel.IPAddress = 'localhost';
    connection.CommunicationChannel.Port = '0';

  8. To register the connectivity in MATLAB, add the target hardware and connection information to an internal database by using the target.add function. By default, the information is only available for the current MATLAB session. To make the registration persist across MATLAB sessions, specify the name-value pair 'UserInstall', true.

    target.add([hostTarget connection], 'UserInstall', true);
  9. You can now specify your development computer as the target hardware for PIL simulations. Before you run a PIL simulation, in the Configuration Parameters dialog box, set Hardware board to Example Intel Board.

Set Up PIL Connectivity for Linux-Based Target Hardware

If you want to set up PIL connectivity for a Linux® target computer, you can use the Linux target.OperatingSystem object, which is associated with prespecified PIL dependencies:

  1. Define the board.

    board = target.create('Board', 'Name', 'Example Intel Board');
    processor = target.create('Processor', 'Name', 'ExampleProcessor');
    processor.LanguageImplementations = target.get('LanguageImplementation', 'Intel-x86-64 (Linux 64)'); 
    board.Processors = processor;
  2. Associate the board with the operating system definition through a target.Support object.

    osSupport = target.create('Support', 'Name', 'OS Support');
    osSupport.For = target.get('OperatingSystem', 'Linux');
    osSupport.RunsOn = board;

Support PIL Debugging

The workflow in Use rtiostream API for PIL Target Connectivity provides PIL connectivity between Simulink and the target hardware. To also support PIL debugging, implement the following steps after or instead of step 3 (Specify execution information for the target hardware):

  1. Create a target.ExecutionService object that references the debugger abstraction interface.

    debugExecutionTool = target.create('ExecutionService', ...
                                    'Name', 'GDB Launch');
    debugExecutionTool.APIImplementation = ...
                                    target.create('APIImplementation', ...
                                    'Name', 'myServiceImplementation');
    debugExecutionTool.APIImplementation.BuildDependencies = ...
                                    target.create('MATLABDependencies');
    debugExecutionTool.APIImplementation.BuildDependencies.Classes = ...
                                    {'myImplementationClass'};
    debugExecutionTool.APIImplementation.API = ...
                                    target.get('API', ...
                                    'DebugExecutionTool');

  2. Associate the target.ExecutionService object with the target.Board object.

    hostTarget.Tools.DebugTools = debugExecutionTool;

DebugExecutionTool Template

This section provides a pseudocode example of target.DebugExecutionTool debugger abstraction. Use a target.DebugExecutionTool object to:

  • Control the lifetime of an application running in the debugger.

  • Automate applying breakpoints.

For interactions with the debugger, you can use MATLAB external language interfaces to APIs, which the debugger vendor supplies.

target.DebugExecutionTool ObjectComments
classdef DebugExecutionToolTemplate < target.DebugExecutionTool

Example implementation of target.DebugExecutionTool debugger abstraction.

    properties (Access=private)

        DebuggerHandle;
    end   

Optional.

Add any state required to interact with the debugger or application being run in the debugger as a property of DebugExecutionTool. Apply private access so that only this file can access the state. For example, the state can be a handle to the debugger application provided by the debugger external API.

    methods

Method implementations that perform debugger interactions. Each method returns a 'errFlag' logical flag. If the method executes, 'errFlag' is set to false. Consider also using the MATLAB error method to report custom error information.

        function this = DebugExecutionToolTemplate()
        
        end

Optional.

Constructor for the target.DebugExecutionTool. The constructor creates an object of the tool allowing its methods to be called from that object.

Perform basic initialization here. For example, setting state for calls to open.

        function errFlag = open(this)

            this.DebuggerHandle = this.callExternalDebugger('open');
            errFlag = false;
        end

Optional.

Add logic here to open the debugger. Typically, use system commands or the external API provided by the debugger.

If you cannot open the debugger without also loading the application into the debugger, do not implement this method. Use the 'loadApplication' method to implement the logic.

If you cannot open the debugger without also starting execution of the application, do not implement this method. Use the 'startApplication' method to implement the logic.

        function errFlag = loadApplication(this)
                        
            this.callExternalDebugger('load application', ...
                                       this.Application, ....
                                       this.ApplicationCommandLineArguments);
            
            this.callExternalDebugger('set breakpoints', this.Breakpoints);
            
            errFlag = false;
        end

Optional.

Load the application into the debugger.

Add logic here to load the application into the debugger. For example, load a project into an IDE debugger or load the application executable file into the debugger harness.

Also add logic to set breakpoints in the debugger to break execution.

The path to the application file is stored in the Application property of this class which can you can access by using this.Application.

Additional command line arguments for the application are stored in this.ApplicationCommandLineArguments.

Breakpoints to set are stored in this.Breakpoints.

If you cannot load the application without also starting execution of the application, do not implement this method. Implement the logic in the 'startApplication' method.

        function errFlag = startApplication(this)
            
            this.callExternalDebugger('start application');
            
            errFlag = false;
        end

Required.

Start the application execution in the debugger.

Add logic here to start the application execution in the debugger.

The path to the application to load is stored in the Application property of this class, which you can access by using this.Application.

Additional command line arguments to pass to the application are stored in this.ApplicationCommandLineArguments.

Breakpoints to set are stored in this.Breakpoints.

        function errFlag = stopApplication(this)
            
            this.callExternalDebugger('stop application');
            
            errFlag = false;
        end

Optional.

Stop the application execution in the debugger.

Add logic here to stop the application execution in the debugger.

If your 'startApplication' method opened the debugger, perform the opposite action here, i.e. close the debugger.

If your 'startApplication' method loaded the application, perform the opposite action here, i.e. unload the application from the debugger.

        function errFlag = unloadApplication(this)
            
            this.callExternalDebugger('unload application');
            
            errFlag = false;
        end

Optional.

Unloads the application from the debugger.

If you implemented the 'loadApplication' method, then add logic here to unload the application from the debugger.

If your 'loadApplication' method opened the debugger, perform the opposite action here, i.e. close the debugger.

        function errFlag = close(this)
            
            this.callExternalDebugger('close');
            
            errFlag = false;
        end              

Optional.

Closes the debugger.

Add logic here to close the debugger. If you implemented the 'open' method, you must also implement the 'close' method.

    end

end statement for methods.

    methods (Access=private)
        
        function output = callExternalDebugger(~, varargin)
            
            output = [];
            fprintf("\nInstructed the debugger to '%s'\n", varargin{1});
            
            if nargin > 2
                disp('Using arguments:');
                disp(varargin(2:end));
            end
        end
    end
Private methods.
end

end statement for classdef.

classdef DebugExecutionToolTemplate < target.DebugExecutionTool
% DEBUGEXECUTIONTOOLTEMPLATE Example implementation of target.DebugExecutionTool
% debugger abstraction.

%   Copyright 2023 The MathWorks, Inc.
    
    properties (Access=private)
        
        % Optional
        % Add any state required to interact with the debugger or
        % application being run in the debugger as a property of
        % DebugExecutionTool. Apply private access so that only this file 
        % can access the state.
        %
        % For example, the state could be a handle to the debugger
        % application provided by the debugger external API.
        DebuggerHandle;
    end   
    
    % Method implementations that perform debugger interactions.
    % Each method will return a 'errFlag' logical flag. If the method 
    % executes, 'errFlag' is set to false.
    % Consider also using the MATLAB error method to report custom 
    % error information.
    methods
                
        % Constructor for the target.DebugExecutionToolTemplate. The constructor
        % creates an instance of the tool allowing its methods to be called
        % on that instance. Optional.
        function this = DebugExecutionToolTemplate()
            
            % Basic initialization can be performed here. For example
            % setting state ready for calls to open
        end
        
        % Open the debugger. Optional.
        function errFlag = open(this)
            
            % Add logic here to open the debugger. Typically, use system
            % commands or the external API provided by the debugger.
            %
            % If you cannot open the debugger without also loading the
            % application into the debugger, this method should not be
            % implemented and the logic should be performed in the
            % 'loadApplication' method.
            %
            % If you cannot open the debugger without also starting
            % execution of the application, this method should not be
            % implemented and the logic should be performed in the
            % 'startApplication' method.
            
            this.DebuggerHandle = this.callExternalDebugger('open');
            errFlag = false;
        end
        
        % Load the application into the debugger. Optional.
        function errFlag = loadApplication(this)
            
            % Add logic here to load the application into the debugger.
            % This could be loading a project into an IDE debugger or
            % loading the application executable into the debugger harness.
            %
            % Additionally add logic to set breakpoints in the debugger to
            % break execution.
            %
            % The path to the application to load is stored in the
            % Application property of this class which can be accessed
            % using this.Application in this method.
            %
            % Additional command line arguments to pass to the 
            % application are stored in 
            % this.ApplicationCommandLineArguments.
            %
            % Breakpoints to set are stored in this.Breakpoints
            %            
            % If you cannot load the application without also starting
            % execution of the application, this method should not be
            % implemented and the logic should be performed in the
            % 'startApplication' method.
            
            this.callExternalDebugger('load application', ...
                                       this.Application, ....
                                       this.ApplicationCommandLineArguments);
            
            this.callExternalDebugger('set breakpoints', this.Breakpoints);
            
            errFlag = false;
        end
        
        % Start the application execution in the debugger. Required.
        function errFlag = startApplication(this)
            
            % Add logic here to start the application execution in the 
            % debugger.
            %
            % The path to the application to load is stored in the
            % Application property of this class which can be accessed
            % using this.Application in this method.
            %
            % Additional command line arguments to pass to the 
            % application are stored in 
            % this.ApplicationCommandLineArguments.
            %
            % Breakpoints to set are stored in this.Breakpoints
            
            this.callExternalDebugger('start application');
            
            errFlag = false;
        end
        
        % Stop the application execution in the debugger. Optional.
        function errFlag = stopApplication(this)
                        
            % Add logic here to stop the application execution in the 
            % debugger.
            
            % If your 'startApplication' method opened the debugger,
            % perform the opposite action here, i.e. close the 
            % debugger.
            %
            % If your 'startApplication' method loaded the application,
            % perform the opposite action here, i.e. unload the
            % application from the debugger.
            
            this.callExternalDebugger('stop application');
            
            errFlag = false;
        end
        
        % Unloads the application from the debugger. Optional.
        function errFlag = unloadApplication(this)
                        
            % If you implemented the 'loadApplication' method, 
            % then add logic here to unload the application from
            % the debugger.
            
            % If your 'loadApplication' method opened the debugger, 
            % perform the opposite action here, i.e. close the 
            % debugger.
            
            this.callExternalDebugger('unload application');
            
            errFlag = false;
        end
        
        % Closes the debugger. Optional.
        function errFlag = close(this)
                        
            % Add logic here to close the debugger. If you implemented the 
            % 'open' method, you should implement this method.
            
            this.callExternalDebugger('close');
            
            errFlag = false;
        end              
        
    end
    
    methods (Access=private)
        
        function output = callExternalDebugger(~, varargin)
            
            output = [];
            fprintf("\nInstructed the debugger to '%s'\n", varargin{1});
            
            if nargin > 2
                disp('Using arguments:');
                disp(varargin(2:end));
            end
        end
    end
end

Use Debugger for PIL Target Connectivity

To provide PIL connectivity between Simulink and the target hardware, you can use the target namespace with a debugger, for example, the GNU® Debugger.

  1. Implement the interface in a class called myImplementationClass. For an example pseudocode implementation, see DebugIOTool Template.

    classdef myImplementationClass < target.DebugIOTool
        properties (Access=private)
        …
        end
         
        methods
        …
        end
    
        methods (Access=private)          
        …
        end
    end

  2. Create target.Board object that provides MATLAB with a description of your target hardware.

    hostTarget = target.create('Board', ...
                               'Name', 'GDB Debug PIL IO');

  3. To support code generation, associate the board with a target.Processor object that contains a language implementation. For this example, create a target.Processor object and reuse an existing target.LanguageImplementation object. For information about setting up a custom target.LanguageImplementation object, see Register New Hardware Devices.

    hostTarget.Processors = target.get('Processor', ...
                                       'Intel-x86-64 (Linux 64)');

  4. Create a target.ExecutionService object that references the debugger abstraction interface.

    matlabExecution = target.create('ExecutionService', ...
                                    'Name', 'GDB Launch');
    matlabExecution.APIImplementation = ...
                                    target.create('APIImplementation', ...
                                    'Name', 'myServiceImplementation');
    matlabExecution.APIImplementation.BuildDependencies = ...
                                    target.create('MATLABDependencies');
    matlabExecution.APIImplementation.BuildDependencies.Classes = ...
                                    {'myImplementationClass'};
    matlabExecution.APIImplementation.API = ...
                                    target.get('API', ...
                                    'DebugIOTool');

  5. Associate the target.ExecutionService object with the target.Board object.

    hostTarget.Tools.DebugTools = matlabExecution;

  6. Add the board object to an internal database and make the object persist across MATLAB sessions.

    target.add(hostTarget, 'UserInstall', true);

  7. Before you run a PIL simulation, in the Configuration Parameters dialog box, set Hardware board to GDB Debug PIL IO.

DebugIOTool Template

This section provides a pseudocode example of target.DebugIOTool debugger abstraction. Use a target.DebugIOTool object to:

  • Control the lifetime of an application running in the debugger.

  • Automate common debugger actions. For example, applying breakpoints and continuing execution from a paused state.

  • Read and write to memory that the application uses.

For interactions with the debugger, you can use MATLAB external language interfaces to APIs, which the debugger vendor supplies.

target.DebugIOTool ObjectComments
classdef DebugIOToolTemplate < target.DebugIOTool

Example implementation of target.DebugIOTool debugger abstraction.

    properties (Access=private)

        DebuggerHandle;
    end   

Optional.

Add any state required to interact with the debugger or application being run in the debugger as a property of DebugIOTool. Apply private access so that only this file can access the state. For example, the state can be a handle to the debugger application provided by the debugger external API.

    methods

Method implementations that perform debugger interactions. Each method returns a 'errFlag' logical flag. If the method executes, 'errFlag' is set to false. Consider also using the MATLAB error method to report custom error information.

        function this = DebugIOToolTemplate()
        
        end

Optional.

Constructor for the target.DebugIOTool. The constructor creates an object of the tool allowing its methods to be called from that object.

Perform basic initialization here. For example, setting state for calls to open.

        function errFlag = open(this)

            this.DebuggerHandle = this.callExternalDebugger('open');
            errFlag = false;
        end

Optional.

Add logic here to open the debugger. Typically, use system commands or the external API provided by the debugger.

If you cannot open the debugger without also loading the application into the debugger, do not implement this method. Use the 'loadApplication' method to implement the logic.

If you cannot open the debugger without also starting execution of the application, do not implement this method. Use the 'startApplication' method to implement the logic.

        function errFlag = loadApplication(this)
                        
            this.callExternalDebugger('load application', ...
                                       this.Application, ....
                                       this.ApplicationCommandLineArguments);
            
            this.callExternalDebugger('set breakpoints', this.Breakpoints);
            
            errFlag = false;
        end

Optional.

Load the application into the debugger.

Add logic here to load the application into the debugger. For example, load a project into an IDE debugger or load the application executable file into the debugger harness.

Also add logic to set breakpoints in the debugger to break execution.

The path to the application file is stored in the Application property of this class which can you can access by using this.Application.

Additional command line arguments for the application are stored in this.ApplicationCommandLineArguments.

Breakpoints to set are stored in this.Breakpoints.

If you cannot load the application without also starting execution of the application, do not implement this method. Implement the logic in the 'startApplication' method.

        function errFlag = startApplication(this)
            
            this.callExternalDebugger('start application');
            
            errFlag = false;
        end

Required.

Start the application execution in the debugger.

Add logic here to start the application execution in the debugger.

The path to the application to load is stored in the Application property of this class, which you can access by using this.Application.

Additional command line arguments to pass to the application are stored in this.ApplicationCommandLineArguments.

Breakpoints to set are stored in this.Breakpoints.

        function errFlag = stopApplication(this)
            
            this.callExternalDebugger('stop application');
            
            errFlag = false;
        end

Optional.

Stop the application execution in the debugger.

Add logic here to stop the application execution in the debugger.

If your 'startApplication' method opened the debugger, perform the opposite action here, i.e. close the debugger.

If your 'startApplication' method loaded the application, perform the opposite action here, i.e. unload the application from the debugger.

        function errFlag = unloadApplication(this)
            
            this.callExternalDebugger('unload application');
            
            errFlag = false;
        end

Optional.

Unloads the application from the debugger.

If you implemented the 'loadApplication' method, then add logic here to unload the application from the debugger.

If your 'loadApplication' method opened the debugger, perform the opposite action here, i.e. close the debugger.

        function errFlag = close(this)
            
            this.callExternalDebugger('close');
            
            errFlag = false;
        end              

Optional.

Closes the debugger.

Add logic here to close the debugger. If you implemented the 'open' method, you must also implement the 'close' method.

        function [atBreakpoint, errFlag] = ...
                                    stoppedAtBreakpoint(this, breakpoint)

            result = this.callExternalDebugger('determine breakpoint hit', ...
                                                breakpoint.File, ...
                                                breakpoint.Line, ...
                                                breakpoint.Function);
            atBreakpoint = ~isempty(result);
            
            errFlag = false;
        end

Required.

Asks if the debugger is currently stopped at the specified breakpoint. Returns true if at the specified breakpoint, false otherwise.

Add logic to determine if the debugger has broken execution at the breakpoint passed into the function.

The 'breakpoint' input variable is of type target.Breakpoint, which provides the description of the breakpoint. It contains properties Function, File, and Line which describes the location of the breakpoint.

Set the atBreakpoint return value to true if the debugger has stopped at the breakpoint, or false otherwise.

        function errFlag = continueExecution(this)
            
            this.callExternalDebugger('continue execution');
            errFlag = false;
        end

Required.

Continues execution if application is paused in the debugger.

        function errFlag = write(this, variableName, memoryStream)            
              
            sizeOfMemoryVariable = uint64(numel(memoryStream));
            arrayFormat = repmat('%d,', [1 sizeOfMemoryVariable]);
            arrayFormat(end) = [];
              
            this.callExternalDebugger(sprintf(['execute (void*)memcpy(%s,' ...
                                      '(unsigned char [%d]){%s}, %d)'], ...
                                      variableName, ...
                                      sizeOfMemoryVariable, ...
                                      sprintf(arrayFormat, memoryStream), ...
                                      sizeOfMemoryVariable));
                          
            errFlag = false;
        end

Write the memory stream to target platform memory specified by the variable name.

Add logic to write memoryStream data to the variable variableName. The function assumes that code execution has reached the breakpoint that requires this action.

memoryStream is a vector of uint64 values. The unit64 values act as a container for the smallest addressable integer on the target platform, which in most cases is the size of char, typically 8 bits.

The variable represents a pointer to the smallest addressable integer type. For example, write operations on a target platform where char represents the smallest addressable integer are equivalent to the memcpy call in C code:

(void*)memcpy(<variableName>, 
              (unsigned char[<sizeOfMemoryStream>]) 
              {<memoryStream(1)>,<memoryStream(2)>}, 
               <sizeOfMemoryStream>);
If your debugger allows immediate execution of code when execution is broken, use the memcpy approach to write data to the variable.

        function [memoryStream, errFlag] = read(this, ...
                                                     variableName, ...
                                                     sizeToRead)            
            
                        
            memoryStream = this.callExternalDebugger(...
                           sprintf("read (unsigned char [%d])*%s", ...
                           sizeToRead, variableName));
            
            memoryStream = uint64(memoryStream);
            
            errFlag = false;
        end

Read the memory stream from target platform memory specified by variable name.

Add logic to read from the variableName variable on the target platform. The function assumes that code execution has reached the breakpoint that requires this action.

The memoryStream return value is a vector of uint64 values. The unit64 values act as a container for the smallest addressable integer on the target platform, which in most cases is the size of char, typically 8 bits.

The variable represents a pointer to the smallest addressable integer type. Read operations are equivalent to this C code:

(unsigned char [<sizeToRead>])*<variableName>
If your debugger allows immediate execution of code when execution is broken, use the C code approach to inspect the value of the buffer and return it to MATLAB.

    end

end statement for methods.

    methods (Access=private)
        
        function output = callExternalDebugger(~, varargin)
            
            output = [];
            fprintf("\nInstructed the debugger to '%s'\n", varargin{1});
            
            if nargin > 2
                disp('Using arguments:');
                disp(varargin(2:end));
            end
        end
    end
Private methods.
end

end statement for classdef.

classdef DebugIOToolTemplate < target.DebugIOTool
% DEBUGIOTOOLTEMPLATE Example implementation of target.DebugIOTool
% debugger abstraction.

%   Copyright 2020 The MathWorks, Inc.
    
    properties (Access=private)
        
        % Optional
        % Add any state required to interact with the debugger or
        % application being run in the debugger as a property of
        % DebugIOTool. Apply private access so that only this file 
        % can access the state.
        %
        % For example, the state could be a handle to the debugger
        % application provided by the debugger external API.
        DebuggerHandle;
    end   
    
    % Method implementations that perform debugger interactions.
    % Each method will return a 'errFlag' logical flag. If the method 
    % executes, 'errFlag' is set to false.
    % Consider also using the MATLAB error method to report custom 
    % error information.
    methods
                
        % Constructor for the target.DebugIOTool. The constructor
        % creates an instance of the tool allowing its methods to be called
        % on that instance. Optional.
        function this = DebugIOToolTemplate()
            
            % Basic initialization can be performed here. For example
            % setting state ready for calls to open
        end
        
        % Open the debugger. Optional.
        function errFlag = open(this)
            
            % Add logic here to open the debugger. Typically, use system
            % commands or the external API provided by the debugger.
            %
            % If you cannot open the debugger without also loading the
            % application into the debugger, this method should not be
            % implemented and the logic should be performed in the
            % 'loadApplication' method.
            %
            % If you cannot open the debugger without also starting
            % execution of the application, this method should not be
            % implemented and the logic should be performed in the
            % 'startApplication' method.
            
            this.DebuggerHandle = this.callExternalDebugger('open');
            errFlag = false;
        end
        
        % Load the application into the debugger. Optional.
        function errFlag = loadApplication(this)
            
            % Add logic here to load the application into the debugger.
            % This could be loading a project into an IDE debugger or
            % loading the application executable into the debugger harness.
            %
            % Additionally add logic to set breakpoints in the debugger to
            % break execution.
            %
            % The path to the application to load is stored in the
            % Application property of this class which can be accessed
            % using this.Application in this method.
            %
            % Additional command line arguments to pass to the 
            % application are stored in 
            % this.ApplicationCommandLineArguments.
            %
            % Breakpoints to set are stored in this.Breakpoints
            %            
            % If you cannot load the application without also starting
            % execution of the application, this method should not be
            % implemented and the logic should be performed in the
            % 'startApplication' method.
            
            this.callExternalDebugger('load application', ...
                                       this.Application, ....
                                       this.ApplicationCommandLineArguments);
            
            this.callExternalDebugger('set breakpoints', this.Breakpoints);
            
            errFlag = false;
        end
        
        % Start the application execution in the debugger. Required.
        function errFlag = startApplication(this)
            
            % Add logic here to start the application execution in the 
            % debugger.
            %
            % The path to the application to load is stored in the
            % Application property of this class which can be accessed
            % using this.Application in this method.
            %
            % Additional command line arguments to pass to the 
            % application are stored in 
            % this.ApplicationCommandLineArguments.
            %
            % Breakpoints to set are stored in this.Breakpoints
            
            this.callExternalDebugger('start application');
            
            errFlag = false;
        end
        
        % Stop the application execution in the debugger. Optional.
        function errFlag = stopApplication(this)
                        
            % Add logic here to stop the application execution in the 
            % debugger.
            
            % If your 'startApplication' method opened the debugger,
            % perform the opposite action here, i.e. close the 
            % debugger.
            %
            % If your 'startApplication' method loaded the application,
            % perform the opposite action here, i.e. unload the
            % application from the debugger.
            
            this.callExternalDebugger('stop application');
            
            errFlag = false;
        end
        
        % Unloads the application from the debugger. Optional.
        function errFlag = unloadApplication(this)
                        
            % If you implemented the 'loadApplication' method, 
            % then add logic here to unload the application from
            % the debugger.
            
            % If your 'loadApplication' method opened the debugger, 
            % perform the opposite action here, i.e. close the 
            % debugger.
            
            this.callExternalDebugger('unload application');
            
            errFlag = false;
        end
        
        % Closes the debugger. Optional.
        function errFlag = close(this)
                        
            % Add logic here to close the debugger. If you implemented the 
            % 'open' method you should implement this method.
            
            this.callExternalDebugger('close');
            
            errFlag = false;
        end              
        
        % Asks if the debugger is currently stopped at the specified
        % breakpoint. Returns true if at the specified breakpoint, false
        % otherwise. Required.
        function [atBreakpoint, errFlag] = ...
                                    stoppedAtBreakpoint(this, breakpoint)
            
            % Add logic to determine if the debugger has broken execution
            % at the breakpoint passed into the function.
            %
            % The 'breakpoint' input variable is of type target.Breakpoint 
            % which provides the description of the breakpoint. It contains
            % properties Function, File and Line which describes the
            % location of the breakpoint.
            %
            % Set the atBreakpoint return value to true if the debugger is
            % stopped at the breakpoint, or false if not.
            
            result = this.callExternalDebugger('determine breakpoint hit', ...
                                                breakpoint.File, ...
                                                breakpoint.Line, ...
                                                breakpoint.Function);
            atBreakpoint = ~isempty(result);
            
            errFlag = false;
        end
        
        % Continues execution if application execution is paused in the 
        % debugger. Required.
        function errFlag = continueExecution(this)
            
            this.callExternalDebugger('continue execution');
            errFlag = false;
        end  
                
        % Write the specified memory stream to the target platform memory
        % specified by the given variable name.
        function errFlag = write(this, variableName, memoryStream)
            
            % Add logic to write the memoryStream data to the variableName
            % variable in the target. This function should assume that the
            % appropriate breakpoint has been hit to perform such an
            % action.
            %
            % memoryStream is a vector of uint64 values to be written to
            % the target platform. The unit64 values act as a container for
            % the smallest addressable integer on the target platform. In
            % most cases this will be a container for the size of char on
            % the target platform, which will commonly be 8-bit.
            %
            % The specified variable will represent a pointer to the
            % smallest addressable integer type.
            
            % As an example write operations on a target platform where 
            % char represents the smallest addressable integer should be 
            % equivalent to the following memcpy call in C code:
            %
            % (void*)memcpy(<variableName>, 
            %               (unsigned char[<sizeOfMemoryStream>])
            %               {<memoryStream(1)>,<memoryStream(2)>, ...},
            %               <sizeOfMemoryStream>);
            %
            % A lot of debuggers allow for immediate execution of code when
            % execution is broken, if so, attempt to use the above command
            % to write data to the variable like the below example:
              
            sizeOfMemoryVariable = uint64(numel(memoryStream));
            arrayFormat = repmat('%d,', [1 sizeOfMemoryVariable]);
            arrayFormat(end) = [];
              
            this.callExternalDebugger(sprintf(['execute (void*)memcpy(%s,' ...
                                      '(unsigned char [%d]){%s}, %d)'], ...
                                      variableName, ...
                                      sizeOfMemoryVariable, ...
                                      sprintf(arrayFormat, memoryStream), ...
                                      sizeOfMemoryVariable));
                          
            errFlag = false;
        end
        
        % read the memory stream from the target platform memory specified 
        % by the given variable name.
        function [memoryStream, errFlag] = read(this, ...
                                                     variableName, ...
                                                     sizeToRead)            
            
            % Add logic to read from the variableName variable on the 
            % target. This function should assume that the appropriate 
            % breakpoint has been hit to perform such an action.
            %
            % The memoryStream return value should be a vector of uint64 
            % values. The unit64 values act as a container for the smallest 
            % addressable integer on the target platform. In most cases 
            % this will be a container for the size of char on the target 
            % platform, which will commonly be 8-bit.
            %
            % The specified variable will represent a pointer to the
            % smallest addressable integer type.
            
            % Read operations should be equivalent to inspecting the result
            % of the following C code:
            % (unsigned char [<sizeToRead>])*<variableName>
            %
            % A lot of debuggers allow for immediate execution of code when
            % execution is broken, if so, attempt to use the above command
            % to inspect the value of the buffer and return it to MATLAB.
                        
            memoryStream = this.callExternalDebugger(...
                           sprintf("read (unsigned char [%d])*%s", ...
                           sizeToRead, variableName));
            
            % The memory stream must be returned as a vector of uint64
            % values
            memoryStream = uint64(memoryStream);
            
            errFlag = false;
        end
    end
    
    methods (Access=private)
        
        function output = callExternalDebugger(~, varargin)
            
            output = [];
            fprintf("\nInstructed the debugger to '%s'\n", varargin{1});
            
            if nargin > 2
                disp('Using arguments:');
                disp(varargin(2:end));
            end
        end
    end
end

See Also

Topics