Code Architecture
Before investigating the specific code generation pieces of
the Target Language Compiler (TLC), consider how Target Language Compiler
generates code for a simple model. From the next figure, you see that
blocks place code into Mdl
routines.
This shows MdlOutputs
.
static void simple_output(int_T tid) { /* Sin Block: '<Root>/Sine Wave' */ simple_B.SineWave_d = simple_P.SineWave_Amp * sin(simple_P.SineWave_Freq * simple_M->Timing.t[0] + simple_P.SineWave_Phase) + simple_P.SineWave_Bias; /* Gain: '<Root>/Gain' */ simple_B.Gain_d = simple_B.SineWave_d * simple_P.Gain_Gain; /* Outport: '<Root>/Out1' */ simple_Y.Out1 = simple_B.Gain_d; }
Blocks have inputs, outputs, parameters, states, plus other general properties. For example,
block inputs and outputs are generally written to a block I/O structure (generated with
identifiers of the type
, where
model
_B
is the model name). Block inputs can
also come from the external input structure
(model
) or the state structure when
connected to a state port of an integrator
(model
_U
), or ground
(model
_XrtGround
) if unconnected or grounded. Block outputs can also go to the
external output structure, (
). The
following diagram shows the general block data mappings.model
_Y
This discussion should give you a general sense of what the block object looks like. Now, you can look at specific pieces of the code generation process that are specific to the Target Language Compiler.
Code Generation Process
The code generator invokes the Target Language Compiler after a model is compiled into a
partial representation of the model
(
) suitable for code generation. To
generate code, the Target Language Compiler uses its library of functions to transform two
classes of target files:model
.rtw
System target files
Block target files
System target files are used to specify the overall structure of the generated code, tailoring for specific target environments. Block target files are used to implement the functionality of Simulink® blocks, including user-defined S-function blocks.
You can create block target files for C MEX, Fortran, and MATLAB® language S-functions to fully inline block functionality into the body of the generated code. C MEX S-functions can be noninlined, wrapper-inlined, or fully inlined. Fortran S-functions must be wrapper-inlined or fully inlined.
How TLC Determines S-Function Inlining Status
Whenever the Target Language Compiler encounters an entry for an S-function block in the
file, it must decide whether to
generate a call to the S-function or to inline it.model
.rtw
Because they cannot use SimStructs
, Fortran and MATLAB language S-functions must be inlined. This inlining can either be in the form
of a full block target file or a one-line block target file that refers to a substitute C
MEX S-function source file.
The Target Language Compiler selects a C MEX S-function for inlining if an explicit
mdlRTW()
function exists in the S-function code or if a target file
for the current target language for the current block is in the TLC file search path. If a C
MEX S-function has an explicit mdlRTW()
function, there must be a
corresponding target file or an error condition results.
The target file for an S-function must have the same root name as the S-function and
must have the extension .tlc
. For example, the target file for a C MEX
S-function named sfix_bitop
has the filename
sfix_bitop.tlc
.
Inlined and Noninlined S-Function Code
This example focuses on a C MEX S-function named sfix_bitop
. The code
generation options are set to allow reuse of signal memory for signal lines that were not
set as tunable signals.
The code generated for the bit-wise operator block reuses a temporary variable that is set up for the output of the sum block to save memory. This results in one very efficient line of code, as seen here.
/* Bitwise Logic Block: <Root>/Bitwise Logical Operator */ /* [input] OR 'F00F' */ rtb_temp2 |= 0xF00F;
Initialization or setup code is not required for this inlined block.
If this block were not inlined, the source code for the S-function itself with its
various options would be added to the generated code base, memory would be allocated in the
generated code for the block’s SimStruct
data, and calls to the
S-function methods would be generated to initialize, run, and terminate the S-function code.
To execute the mdlOutputs
function of the S-function, code would be
generated like this:
/* Level2 S-Function Block: <Root>/Bitwise Logical Operator (sfix_bitop) */ { SimStruct *rts = ssGetSFunction(rtS, 0); sfcnOutputs(rts, tid); }
The entire mdlOutputs
function is called and runs just as it does
during simulation. That’s not everything, though. There is also registration,
initialization, and termination code for the noninlined S-function. The initialization and
termination calls are similar to the fragment above. Then, the registration code for an
S-function with just one inport and one outport is 72 lines of C code generated as part of
file
.model
_reg.h
/*Level2 S-Function Block: <Root>/Bitwise Logical Operator (sfix_bitop) */ { extern void untitled_sf(SimStruct *rts); SimStruct *rts = ssGetSFunction(rtS, 0); /* timing info */ static time_T sfcnPeriod[1]; static time_T sfcnOffset[1]; static int_T sfcnTsMap[1]; { int_T i; for(i = 0; i < 1; i++) { sfcnPeriod[i] = sfcnOffset[i] = 0.0; } } ssSetSampleTimePtr(rts, &sfcnPeriod[0]); ssSetOffsetTimePtr(rts, &sfcnOffset[0]); ssSetSampleTimeTaskIDPtr(rts, sfcnTsMap); ssSetMdlInfoPtr(rts, ssGetMdlInfoPtr(rtS)); /* inputs */ { static struct _ssPortInputs inputPortInfo[1]; _ssSetNumInputPorts(rts, 1); ssSetPortInfoForInputs(rts, &inputPortInfo[0]); /* port 0 */ { static real_T const *sfcnUPtrs[1]; sfcnUPtrs[0] = &rtU.In1; ssSetInputPortSignalPtrs(rts, 0, (InputPtrsType)&sfcnUPtrs[0]); _ssSetInputPortNumDimensions(rts, 0, 1); ssSetInputPortWidth(rts, 0, 1); } } . . .
This continues until S-function sizes and methods are declared, allocated, and initialized. The amount of registration code generated is essentially proportional to the number and size of the input ports and output ports.
A noninlined S-function will typically have a significant impact on the size of the generated code, whereas an inlined S-function can be close to the handwritten size and performance of the generated code.