内联 S-Function
内联 S-Function
内联 S-Function 表示需要为 S-Function 模块提供 TLC 文件,该文件将替换在仿真期间使用的 C、C++、Fortran 或 MATLAB® 语言版本的模块。
非内联 S-Function
如果没有提供内联 TLC 文件,大多数目标通过为该模块重新编译 C MEX S-Function 来支持该模块。您使用 C/C++ 编码的 S-Function 和受代码生成器上下文支持的一个 mx*
API 调用有限子集时,会有内存使用量和速度方面的开销。如果您需要最高效的生成代码,您必须通过为它们编写 TLC 文件来内联 S-Function。
当仿真需要执行 S-Function 模块的函数之一时,它会为该函数调用 MEX 文件。当代码生成器执行非内联 S-Function 时,它以类似的方式执行此操作,如下图所示。
内联的类型
定义两个内联类别会很有帮助:
完全内联 S-Function
包装器内联 S-Function
这两种方法都能内联 S-Function 和消除非内联 S-Function 的开销,但仍有所不同。下面的第一个示例使用 timestwo.tlc
,它被视为完全内联 TLC 文件,其中模块的完整实现包含在模块的 TLC 文件中。
第二个示例使用包装器 TLC 文件。此示例并不原位生成算法代码,而是调用包含代码体的 C 函数。使用包装器 TLC 文件有几个潜在好处:
它提供一种使 C MEX S-Function 和生成的代码共享 C 代码的方法。您不需要编写两次代码。
调用的 C 函数是一个优化的例程。
模型中可能存在几个模块,从代码大小的角度来看,在模块中调用函数比为每个模块创建相同的算法代码更高效。
它提供一种将现有 C 代码无缝合并到生成代码中的方法。
完全内联 S-Function 示例
内联 S-Function 提供一种将 S-Function 模块的代码直接嵌入到为模型生成的代码中的机制。代码并不通过函数指针调用单独的源文件并为其维护单独的数据结构体 (SimStruct
),因此代码看起来是“内联的”,如下图所示。
S-Function timestwo.c
提供完全内联 S-Function 的一个简单示例。此模块将其输入乘以 2 并输出结果。该模块的 C MEX 版本在文件
中,该模块的内联 TLC 文件是 matlabroot
/toolbox/simulink/sfuntemplates/src/timestwo.c
。matlabroot
/toolbox/simulink/sfuntemplates/tlc_c/timestwo.tlc
timestwo.tlc
%implements "timestwo" "C" %% Function: Outputs ========================================== %% %function Outputs(block, system) Output /* %<Type> Block: %<Name> */ %% /* Multiply input by two */ %assign rollVars = ["U", "Y"] %roll idx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars %<LibBlockOutputSignal(0, "", lcv, idx)> = \ %<LibBlockInputSignal(0, "", lcv, idx)> * 2.0; %endroll %endfunction
TLC 模块分析
TLC 模块文件需要 %implements
指令,目标语言编译器使用该指令来验证模块类型和模块支持的语言。%function
指令以函数声明开始,并显示函数的名称 Outputs
以及传递给函数的参量 block
和 system
。这些是此模块实例的
文件中的相关记录。model
.rtw
原型的最后一部分是 Output
。这意味着任何不属于 TLC 指令的行都由函数输出到在 TLC 中选择的当前文件。因此,Outputs
函数中的非指令行成为该模块的生成代码。
此 TLC 模块示例中最复杂的部分是 %roll
指令。TLC 使用此指令自动生成 for
循环,具体取决于输入/输出宽度以及输入在内存中是否连续。此示例使用从 roll 函数体内访问输出和输入的典型形式,即使用 LibBlockOutputSignal
和 LibBlockInputSignal
访问输出和输入并执行乘法和赋值。请注意,此 TLC 文件支持任何有效的信号宽度。
用于实现此模块的唯一函数是 Outputs
。对于更复杂的模块,还要声明其他函数。您可以在文件夹
(打开)和 matlabroot
/toolbox/simulink/sfuntemplates/tlc_c
(打开)中找到更复杂的内联 TLC 文件的示例,或者通过查看文件夹 matlabroot
/toolbox/simulink/blocks/tlc_c
(打开)中内置模块的代码来了解此情形。matlabroot/rtw/c/tlc/blocks
timestwo 模型
此简单模型使用 timestwo
S-Function 并显示生成的
文件中的 model
.cMdlOutputs
函数,此文件包含内联 S-Function 代码。
模型输出代码
/* Model output function */ static void timestwo_ex_output(int_T tid) { /* S-Function Block: <Root>/S-Function */ /* Multiply input by two */ timestwo_ex_B.timestwo_output = timestwo_ex_P.Constant_Value * 2.0; /* Outport: '<Root>/Out1' */ timestwo_ex_Y.Out1 = timestwo_ex_B.timestwo_output; }
包装器内联 S-Function 示例
下图展示了一个作为包装器的内联 S-Function。算法是直接从生成的模型代码中调用的,消除了 S-Function 开销,但保留用户函数。
这是 timestwo
模块的包装器版本的内联 TLC 文件。
%implements "timestwo" "C" %% Function: BlockTypeSetup ================================== %% %function BlockTypeSetup(block, system) void %% Add function prototype to model's header file %<LibCacheFunctionPrototype... ("extern void mytimestwo(real_T* in,real_T* out,int_T els);")> %% Add file that contains "myfile" to list of files to be compiled %<LibAddToModelSources("myfile")> %endfunction %% Function: Outputs ========================================== %% %function Outputs(block, system) Output /* %<Type> Block: %<Name> */ %assign outPtr = LibBlockOutputSignalAddr(0, "", "", 0) %assign inPtr = LibBlockInputSignalAddr(0, "", "",0) %assign numEls = LibBlockOutputSignalWidth(0) /* Multiply input by two */ mytimestwo(%<inPtr>,%<outPtr>,%<numEls>); %endfunction
分析
对模型中每种类型的模块调用一次函数 BlockTypeSetup
;它不像 Outputs
函数那样直接生成输出。使用 BlockTypeSetup
在
文件中包含函数原型,并告诉编译过程编译一个附加文件 model
.hmyfile.c
。
Outputs
函数现在调用函数 mytimestwo
,而不是直接执行乘法。模型中此模块的所有实例都调用同一个函数来执行乘法。生成的模型函数 MdlOutputs
则变为
static void timestwo_ex_output(int_T tid) { /* S-Function Block: <Root>/S-Function */ /* Multiply input by two */ mytimestwo(&model_B.Constant_Value,&model_B.S_Function,1); /* Outport Block: <Root>/Out1 */ model_Y.Out1 = model_B.S_Function; }