Set Up PIL Connectivity by Using Target Framework - MATLAB & Simulink (original) (raw)

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:

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.

Workflow When to use
target namespace withrtiostream 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 andDebugExecutionTool 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 andDebugIOTool 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 the board description
    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. Associate the board with a processor
To support code generation, associate the board with atarget.Processor object that contains a language implementation. For this example, create atarget.Processor object and reuse an existingtarget.LanguageImplementation object. For information about setting up a customtarget.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 newtarget.Processor object by using theProcessors property
hostTarget.Processors = processor; 3. Specify execution information for the target hardware
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 theHostProcessExecutionTool orSystemCommandExecutionTool class.
applicationExecution = target.create('HostProcessExecutionTool');
applicationExecution.Name = 'Windows Application';
Create a 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 the communication interface for the target hardware
Create a CommunicationInterface object that provides the target hardware with details of the communication channel and the rtiostream API implementation.
Use:

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. Specify PIL protocol information
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, defaulttarget.PILProtocol values are used. 6. Add profiling timer
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. Specify connection between development computer and target hardware
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 aTargetConnection object. Specify:

  1. Make board and connection objects persist across MATLAB sessions
    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);
  2. Specify board for model
    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.

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. Describe the debugger execution service implementation
    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 debugger execution service with the board
Associate the target.ExecutionService object with the target.Board object.
hostTarget.Tools.DebugTools = debugExecutionTool;

DebugExecutionTool Template

This section provides a pseudocode example oftarget.DebugExecutionTool debugger abstraction. Use atarget.DebugExecutionTool object to:

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

Section Descriptions for target.DebugExecutionTool Debugger Abstraction Example

target.DebugExecutionTool Object Comments
classdef DebugExecutionToolTemplate < target.DebugExecutionTool Example implementation oftarget.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 tofalse. Consider also using the MATLAB error method to report custom error information.
function this = DebugExecutionToolTemplate() end Optional.Constructor for thetarget.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 usingthis.Application.Additional command line arguments for the application are stored inthis.ApplicationCommandLineArguments.Breakpoints to set are stored inthis.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 theApplication property of this class, which you can access by usingthis.Application.Additional command line arguments to pass to the application are stored inthis.ApplicationCommandLineArguments.Breakpoints to set are stored inthis.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 formethods.
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 forclassdef.

Pseudocode for target.DebugExecutionTool Debugger Abstraction Example

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 target.DebugIOTool debugger abstraction interface
    Implement the interface in a class calledmyImplementationClass. 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 the board description
Create target.Board object that provides MATLAB with a description of your target hardware.
hostTarget = target.create('Board', ...
'Name', 'GDB Debug PIL IO'); 3. Associate the board with a processor
To support code generation, associate the board with atarget.Processor object that contains a language implementation. For this example, create atarget.Processor object and reuse an existingtarget.LanguageImplementation object. For information about setting up a customtarget.LanguageImplementation object, see Register New Hardware Devices.
hostTarget.Processors = target.get('Processor', ...
'Intel-x86-64 (Linux 64)'); 4. Describe the debugger execution service implementation
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 debugger execution service with the board
Associate the target.ExecutionService object with the target.Board object.
hostTarget.Tools.DebugTools = matlabExecution; 6. Make board object persist across MATLAB sessions
Add the board object to an internal database and make the object persist across MATLAB sessions.
target.add(hostTarget, 'UserInstall', true); 7. Specify board for model
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 oftarget.DebugIOTool debugger abstraction. Use atarget.DebugIOTool object to:

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

Section Descriptions for target.DebugIOTool Debugger Abstraction Example

target.DebugIOTool Object Comments
classdef DebugIOToolTemplate < target.DebugIOTool Example implementation oftarget.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 tofalse. Consider also using the MATLAB error method to report custom error information.
function this = DebugIOToolTemplate() end Optional.Constructor for thetarget.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 toopen.
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 usingthis.Application.Additional command line arguments for the application are stored inthis.ApplicationCommandLineArguments.Breakpoints to set are stored inthis.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 theApplication property of this class, which you can access by usingthis.Application.Additional command line arguments to pass to the application are stored inthis.ApplicationCommandLineArguments.Breakpoints to set are stored inthis.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. Returnstrue 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 propertiesFunction, File, and Line which describes the location of the breakpoint.Set theatBreakpoint return value totrue 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 variablevariableName. The function assumes that code execution has reached the breakpoint that requires this action.memoryStream is a vector of uint64 values. Theunit64 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 wherechar represents the smallest addressable integer are equivalent to thememcpy call in C code:(void*)memcpy(, (unsigned char[]) {<memoryStream(1)>,<memoryStream(2)>}, );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 ofchar, typically 8 bits.The variable represents a pointer to the smallest addressable integer type. Read operations are equivalent to this C code:(unsigned char [])*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 formethods.
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 forclassdef.

Pseudocode for target.DebugIOTool Debugger Abstraction Example

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

target namespace

Topics