Debug Your TLC Code
tlcdebug
Tutorial Overview
Objective: Introduces the TLC debugger. You will learn how to set breakpoints and familiarize yourself with TLC debugger commands.
Open the Example:
openExample('simulinkcoder/AdviceAboutTLCTutorialsExample') cd('tlctutorial/tlcdebug')
You can cause the TLC debugger to be invoked whenever the build process is invoked. In
this tutorial, you use it to detect a bug in a .tlc
file for a model
called simple_log
. The bug causes the generated code output from the
standalone version of the model to differ from its simulation output. The tutorial guides
you through following steps:
Getting Started — Run the model and inspect output
Generate and Run Code from the Model — Compare compiled results to original output
Start the Debugger and Use Its Commands — Things you can do with the debugger
Debug
timesN.tlc
— Find out what went wrongFix the Bug and Verify — Easy ways to fix bugs and verify fixes
Getting Started
In the MATLAB® Command Window, create a MEX-file for the S-function:
mex timesN.c
This avoids picking up the version shipped with your Simulink® software.
Note
An error might occur if you have not previously run
mex -setup
.Open the model
simple_log
. The model looks like this.In the Data Import/Export pane of the Configuration Parameters dialog box, check Time and Output. This causes model variables to be logged to the MATLAB workspace.
Run the model. On the Simulation tab, click Run. Variables
tout
andyout
appear in your MATLAB workspace.Double-click
yout
in the Workspace pane of the MATLAB Command Window. The Variable Editor displays the 6x1 array output fromsimple_log
. The display looks like this:Column 1 contains discrete pulse output for six time steps (3s and 0s), collected at port
out1
.
Next, you generate a standalone version of simple_log
. You execute it
and compare its results to the output from Simulink displayed above.
Note
For the purpose of this exercise, the TLC file provided,
timesN.tlc
, contains a bug. This version must be in the same folder as
the model that uses it.
Generate and Run Code from the Model
Press Ctrl+B.
The code generator produces, compiles, and links C source code. The MATLAB Command Window shows the progress of the build, which ends with these messages:
### Created executable: simple_log.exe ### Successful completion of build procedure for model: simple_log
Run the standalone model just created by typing
!simple_log
This results in the messages
** starting the model ** ** created simple_log.mat **
Inspect results by placing the variables in your workspace. In the Current Folder pane, double-click
simple_log.mat
, then double-clickrt_yout
(the standalone version of variableyout
) in the Workspace pane.Compare
rt_yout
withyout
. Do you notice differences? Can you surmise what caused values inrt_yout
to change?A look at the generated C code that TLC placed in your build folder (
simple_log_grt_rtw
) helps to identify the problem.Edit
simple_log.c
and look at itsMdlOutputs
function, which should appear as shown below:/* Model output function */ static void simple_log_output(void) { /* DiscretePulseGenerator: '<Root>/Discrete Pulse Generator' */ simple_log_B.DiscretePulseGenerator = (simple_log_DW.clockTickCounter < 1.0) && (simple_log_DW.clockTickCounter >= 0) ? 1.0 : 0.0; if (simple_log_DW.clockTickCounter >= 2.0 - 1.0) { simple_log_DW.clockTickCounter = 0; } else { simple_log_DW.clockTickCounter++; } /* End of DiscretePulseGenerator: '<Root>/Discrete Pulse Generator' */ /* S-Function (timesN): '<Root>/Gain1st' incorporates: * Outport: '<Root>/Out1' */ /* S-Function Block: <Root>/Gain1st */ /* Multiply input by 3.0 */ simple_log_Y.Out1 = simple_log_B.DiscretePulseGenerator * 1; }
Note the line near the end:
simple_log_B.first_output = simple_log_B.DiscretePulseGenerator * 1;
Start the Debugger and Use Its Commands
You use the TLC debugger to monitor the code generation process. As it is not invoked by default, you need to request the debugger explicitly.
Set up the TLC debugging environment and start to build the application:
Select the Configuration Parameters > Code Generation pane, and select the options Retain .rtw file and Start TLC debugger when generating code. Click OK.
Build the model.
The MATLAB Command Window describes the building process. The build stops at the
timesN.tlc
file and displays the command prompt:TLC-DEBUG>
Type
help
to list the TLC debugger commands. Here are some things you can do in the debugger.View and query various entities in the TLC scope.
TLC-DEBUG> whos CompiledModel TLC-DEBUG> print CompiledModel.NumSystems TLC-DEBUG> print TYPE(CompiledModel.NumSystems)
Examine the statements in your current context.
TLC-DEBUG> list TLC-DEBUG> list 10,40
Move to the next line of code.
TLC-DEBUG> next
Step into a function.
TLC-DEBUG> step
Assign a constant value to a variable, such as the input signal
%<u>
.TLC-DEBUG> assign u = 5.0
Set a breakpoint where you are or in some other part of the code.
TLC-DEBUG> break timesN.tlc:10
Execute until the next breakpoint.
TLC-DEBUG> continue
Clear breakpoints you have established.
TLC-DEBUG> clear 1 TLC-DEBUG> clear all
If you have tried the TLC debugger commands, execute the remaining code to finish the build process, then build
simple_log
again. The build stops at thetimesN.tlc
file and displays the command prompt:TLC-DEBUG>
Debug timesN.tlc
Now look around to find out what is wrong with the code:
Set a breakpoint on line 20 of
timesN.tlc
.TLC-DEBUG> break timesN.tlc:20
Instruct the TLC debugger to advance to your breakpoint.
TLC-DEBUG> continue
TLC processes input, reports its progress, advances to line 20 in
timesN.tlc
, displays the line, and pauses.### Loading TLC function libraries ... ### Initial pass through model to cache user defined code . ### Caching model source code . Breakpoint 1 00020: %roll idx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars
Use the
whos
command to see the variables in the current scope.TLC-DEBUG> whos Variables within: <BLOCK_LOCAL> gain Real rollVars Vector block Resolved system Resolved
Inspect the variables using the print command (names are case sensitive).
TLC-DEBUG> print gain 3.0 TLC-DEBUG> print rollVars [U, Y]
Execute one step.
TLC-DEBUG> step 00021: %<LibBlockOutputSignal(0, "", lcv, idx)> = \
Because it is a built-in function, advance via the
next
command.TLC-DEBUG> next . 00022: %<LibBlockInputSignal(0, "", lcv, idx)> * 1;
This is the origin of the C statement responsible for the erroneous constant output,
simple_log_B.first_output = simple_log_B.DiscretePulseGenerator * 1;
.Abandon the build by quitting the TLC debugger. Type
TLC-DEBUG> quit
An error message is displayed showing that you stopped the build by using the TLC debugger
quit
command. Close the error window.
Fix the Bug and Verify
The problem you identified is caused by evaluating a constant rather than a variable
inside the TLC function FcnEliminateUnnecessaryParams()
. This is a
typical coding error and is easily repaired. Here is the code you need to fix.
%function Outputs(block, system) Output %assign gain =SFcnParamSettings.myGain /* %<Type> Block: %<Name> */ %% /* Multiply input by %<gain> */ %assign rollVars = ["U", "Y"] %roll idx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars %<LibBlockOutputSignal(0, "", lcv, idx)> = \ %<LibBlockInputSignal(0, "", lcv, idx)> * 1; %endroll %endfunction %% [EOF] timesN.tlc
To fix the coding error, edit
timesN.tlc
. The linemultiplies the evaluated input by 1. Change the line to%<LibBlockInputSignal(0, "", lcv, idx)> * 1;
%<LibBlockInputSignal(0, "", lcv, idx)> * %<gain>;
Save
timesN.tlc
.Build the standalone model again. Complete the build by typing
continue
at eachTLC-DEBUG>
prompt.Execute the standalone model by typing
A new version of!simple_log
simple_log.mat
is created containing its output.Load
simple_log.mat
and compare the workspace variablert_yout
withyout
, as you did before. The values in the first column should now correspond.