主要内容

内联C-MEX S函数

内联S函数概述

当一个模型金宝app®模型包含一个s函数和对应的TLC块目标文件存在,代码生成器内联s函数。内联s函数可以通过消除生成代码中的s函数API层来生成更高效的代码。

对于可以执行各种任务的s -函数,内联它们使您有机会仅为块的每个实例的当前操作集模式生成代码。作为一个例子,如果一个函数接受任意信号宽度和遍历每个元素的信号,你想生成内联代码循环信号有两个或两个以上的元素,但生成一个简单nonlooped计算当信号只有一个元素。

没有内联的1级C MEX s -函数(写入s -函数API的旧形式)将导致生成的代码调用所有这些函数,即使对于特定的s -函数例程是空的。

函数 目的

mdlInitializeSizes

初始化大小数组

mdlInitializeSampleTimes

初始化示例times数组

临床条件

初始化状态

计算输出

计算输出

mdlUpdate

更新离散状态

mdlDerivatives

计算连续状态的导数

mdlTerminate

当模拟结束时进行清理

没有内联的2级C MEX s -函数(即那些写入当前s -函数API的函数)调用上述函数,但有以下例外:

  • 临床条件仅当MDL_INITIALIZE_CONDITIONS宣布与#定义

  • mdlStart仅当MDL_开始宣布与#定义

  • mdlUpdate仅当MDL_更新宣布与#定义

  • mdlDerivatives仅当MDL_衍生物宣布与#定义

通过内联S函数,可以消除对模拟循环中这些可能为空函数的调用。这可以大大提高生成代码的效率。

内联调用的s函数sfunc_名字,您将创建一个自定义的S-function块目标文件,名为sfunc_name.tlc并将其放在与s功能的mex文件相同的文件夹中。然后,在构建时执行目标文件,而不是在s -函数中设置函数调用.c文件S函数目标文件通过指示目标语言编译器仅插入目标文件中定义的语句来“内联”S函数。

一般来说,内联s函数在以下情况下特别有用

  • 与调用s函数所需的开销相比,执行s函数内容所需的时间很小。

  • 某些s函数例程是空的(例如,mdlUpdate).

  • s函数的行为在仿真和代码生成之间发生变化。例如,设备驱动I/O s函数可以从MATLAB中读取®工作区,但从生成代码中的实际硬件地址读取。

功能参数

S函数可以将两种不同类型的参数写入模型.rtw目标语言编译器文件访问:

  • 参数设置:这些参数对应于通过mdlRTWs函数的方法SSWriterWParamSettings. 然后,S功能TLC实现文件可以直接从SFcnParamSettings在区块中记录。

  • 可调参数:当在s函数中将这类参数注册为运行时参数时,可以访问它们。注意,这些可调参数会自动写入模型.rtw文件。在s -函数的TLC文件中,可以使用LibBlockParameter库函数及其变体。

有关如何创建和使用运行时参数的更多信息,请参见创建和更新s -函数运行时参数.另请参见示例sfcndemo_runtime在S函数示例中,介绍如何创建和使用这两类参数

S函数的示例代码

假设您有一个简单的s函数,它模拟增益块,具有一个输入、一个输出和一个标量增益。也就是说,Y = u * p.如果Simul金宝appink块的名字是喷火二级S函数的名称为食物增益,C MEX S函数必须包含以下代码:

#define S_FUNCTION_NAME foogain #define S_FUNCTION_LEVEL 2 #include " SimStruct .h" #define GAIN mxGetPr(ssGetSFcnParam(S,0))[0] static void mdlinitializesize (SimStruct *S) {ssSetNumContStates (S,0);ssSetNumDiscStates (S, 0);如果(!ssSetNumInputPorts(年代,1))返回;sssetinputporttwidth (S, 0, 1); / /连接端口ssSetInputPortDirectFeedThrough (0, 1);如果(!ssSetNumOutputPorts(年代,1))返回;sssetoutputporttwidth (S, 0, 1); / /输出ssSetNumSFcnParams (S, 1); ssSetNumSampleTimes (S, 0); ssSetNumIWork (S, 0); ssSetNumRWork (S, 0); ssSetNumPWork (S, 0); } static void mdlOutputs(SimStruct *S, int_T tid) { real_T *y = ssGetOutputPortRealSignal(S, 0); const InputRealPtrsType u = ssGetInputPortRealSignalPtrs(S, 0); y[0] = (*u)[0] * GAIN; } static void mdlInitializeSampleTimes(SimStruct *S){} static void mdlTerminate(SimStruct *S) {} #define MDL_RTW /* Change to #undef to remove function */ #if defined(MDL_RTW)&&(defined(MATLAB_MEX_FILE)||defined(NRT)) static void mdlRTW (SimStruct *S) { if (!ssWriteRTWParameters(S, 1,SSWRITE_VALUE_VECT,"Gain","", mxGetPr(ssGetSFcnParam(S,0)),1)) { return; } } #endif #ifdef MATLAB_MEX_FILE #include "simulink.c" #else #include "cg_sfun.h" #endif

以下两个部分显示了生成的模型.c包含S函数的非内联和内联版本食物增益. 该模型不包含其他Simulink块。金宝app

有关这些与S函数相关的C库函数的更多信息,请参见配置C/C++S函数功能.有关如何生成代码的信息,请参见配置模型和生成代码从Simulink模型生成代码的方法金宝app

model.c的非内联和内联版本的比较

如果没有TLC文件来定义s -函数细节,代码生成器必须通过s -函数API调用mex -文件s -函数。下面的代码是模型.c文件的非线性s -函数(即,不存在相应的TLC文件)。

非线性S函数

/ * *模型…*/ real_T untitled_RGND = 0.0;/* real_T ground */ /*启动模型*/ void MdlStart(void) {/* (no Start code required) */} /*计算块输出*/ void MdlOutputs(int_T tid) {/* Level2 S-Function block: /S-Function (foogain) */ {SimStruct *rts = ssGetSFunction(rts, 0);sfcnOutputs (rts, tid);}} /*执行模型更新*/ void MdlUpdate(int_T tid){/*(不需要更新代码)*/}/*终止函数*/ void MdlTerminate(void) {/* Level2 S-Function Block: /S-Function (foogain) */ {SimStruct *rts = ssGetSFunction(rts, 0);sfcnTerminate (rts);}} #include "模型_注册h”/*[EOF]模型c * /

内联函数。这个代码是模型.c食物增益S函数完全内联:

/ * *模型.c…*/*启动模型*/void MdlStart(void){/*(无需启动代码)*/}/*计算块输出*/void MdlOutputs(int_T tid)/*S函数块:/S函数*/*注意:在的内联版本中没有对S函数API的调用模型. c。* / rtB。S_Function = 0.0 * rtP.S_Function_Gain;} /*执行模型更新*/ void MdlUpdate(int_T tid) {/* (no update code required) */} /* Terminate function */ void MdlTerminate(void) {/* (no Terminate code required) */} #include "模型_注册h”/*[EOF]模型c * /

如果为这个S-function块包含这个目标文件,则结果模型.c代码是

rtB。S_Function = 0.0 * rtP.S_Function_Gain;

包含TLC文件极大地减少了代码大小,并提高了生成代码的执行效率。这些注释突出显示了TLC代码和生成的输出的一些信息:

  • TLC指令%实现是块目标文件所需要的,并且必须是块目标文件中的第一个可执行语句。该指令防止目标语言编译器执行不适当的S-function目标文件食物增益

  • 的输入喷火rtGROUND(一个金宝app仿真软件编码器™全局值等于0.0),因为喷火是模型中唯一的块,其输入未连接。

  • 包括TLC文件食物增益消除了对s功能注册段的需要食物增益.这大大减少了代码的大小。

  • TLC代码内联获得参数,当构建过程配置为内联参数值时。例如,在S-function对话框中指定S-function参数为2.5,TLC输出函数生成

    rtB。喷火= input * 2.5;
  • 使用%生成文件指令,如果您的操作系统有文件名大小限制,并且s -函数的名称是foosfunction(超出限制)。在这种情况下,您将在系统目标文件中包括以下语句(引用此S功能块目标文件之前的任何位置)。

    %generatefile foosfunction“foosfunc.tlc”

    此语句告诉目标语言编译器打开foosfunc.tlc而不是foosfunction.tlc

model_reg.h的非联机和内联版本的比较

内联2级s功能显著减少了尺寸模型_注册h代码。模型配准函数很长;本例中删除了许多代码。的内联版本和内联版本之间的区别模型_注册h;内联消除了以下代码:

/ * *模型_reg.h * */ /*独立于s -函数的正常模型初始化代码*/ /*子s -函数注册*/ ssSetNumSFunctions(rtS, 1);/*注册每个子函数*/ {static SimStruct childfunctions [1];静态SimStruct * childSFunctionPtrs [1];(void)memset((char_T *)&childSFunctions[0], 0, sizeof(childSFunctions));ssSetSFunctions (rtS, &childSFunctionPtrs [0]);{int_T我;(我= 0;我< 1;i++) {ssSetSFunction(rtS, i, &childSFunctions[i]);}} /* Level2 S-Function Block: untitled//S-Function (foogain) */ {extern void foogain(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] = &untitled_RGND; ssSetInputPortWidth(rts, 0, 1); ssSetInputPortSignalPtrs(rts, 0, (InputPtrsType)&sfcnUPtrs[0]); } } /* outputs */ { static struct _ssPortOutputs outputPortInfo[1]; _ssSetNumOutputPorts(rts, 1); ssSetPortInfoForOutputs(rts, &outputPortInfo[0]); ssSetOutputPortWidth(rts, 0, 1); ssSetOutputPortSignal(rts, 0, &rtB.S_Function); } /* path info */ ssSetModelName(rts, "S-Function"); ssSetPath(rts, "untitled/S-Function"); ssSetParentSS(rts, rtS); ssSetRootSS(rts, ssGetRootSS(rtS)); ssSetVersion(rts, SIMSTRUCT_VERSION_LEVEL2); /* parameters */ { static mxArray const *sfcnParams[1]; ssSetSFcnParamsCount(rts, 1); ssSetSFcnParamsPtr(rts, &sfcnParams[0]); ssSetSFcnParam(rts, 0, &rtP.S_Function_P1Size[0]); } /* registration */ foogain(rts); sfcnInitializeSizes(rts); sfcnInitializeSampleTimes(rts); /* adjust sample time */ ssSetSampleTime(rts, 0, 0.2); ssSetOffsetTime(rts, 0, 0.0); sfcnTsMap[0] = 0; /* Update the InputPortReusable and BufferDstPort flags for each input port */ ssSetInputPortReusable(rts, 0, 0); ssSetInputPortBufferDstPort(rts, 0, -1); /* Update the OutputPortReusable flag of each output port */ } }

一个TLC文件到内联S-Function foogain

为避免对S函数进行不必要的调用,并生成S函数所需的最低代码,以下TLC文件:,食品添加剂的例子。

%implements "foogain" "C" %function Outputs (block, system) Output /* % block: % */ %% %% assign y = LibBlockOutputSignal (0, "", "", 0) %assign u = LibBlockInputSignal (0, "", "", 0) %assign p = LibBlockParameter (Gain, "", "", 0) % = % * %

;% endfunction

用代码生成的眼光管理块实例数据

实例数据是Simulink模型中块的每个实例所特有的额外数据或工作内存。这不包括参数或状态数据(分别存储在模型参数和状态向量中),而是用于缓存中间结果或参数和模式的派生表示。实例数据的一个金宝app示例是传输延迟块使用的缓冲区。

在第2级s -函数中,可以通过以下几种方式逐个实例分配和使用内存ssSetUserData,工作向量(例如,ssSetRWorkValue,ssSetIWorkValue),或数据类型的工作向量称为德沃克向量。编写S-function和块目标文件的最小努力,以及自动符合静态和malloc目标的实例数据,例如,在使用实例数据编写s -函数时使用数据类型的工作向量。

好处是双重的。首先,编写s函数更简单,因为内存分配和释放是由Simulink处理的。金宝app第二,德沃克向量被写入到模型.rtw文件自动为您,包括德沃克名称、数据类型和大小。这使得编写块目标文件更容易,因为您不必编写TLC代码来分配和释放块目标文件德沃克内存。

此外,如果您想要绑定的组德沃克将向量放入结构中传递给函数,可以用指向的指针填充结构德沃克两个S函数中的数组mdlStart函数和块目标文件的开始方法,实现s函数与生成代码处理数据的一致性。

最后,使用德沃克使得为与s -函数中的实现相匹配的每个块实例创建特定版本的代码(数据类型、标量vs.向量化等)变得简单。两个实现使用德沃克以同样的方式使内联代码可以与金宝appSimulink加速器™软件,而不改变C MEX s -函数或块目标文件。

的内联代码金宝app加速器软件

默认情况下,金宝appSimulink加速器软件调用C MEX S函数作为加速模型模拟的一部分。如果希望在运行加速模型之前让加速器内联S函数,请告诉加速器使用块目标文件将S函数与SS_OPTION_USE_TLC_WITH_ACCELERATOR在呼叫中标记ssSetOptions ()mdlInitializeSizess函数的函数。

注意,TLC生成的代码和C MEX s -函数的内存和工作向量大小和使用量必须相同金宝appSimulink加速器软件无法正确执行内联代码。这是因为调用cmex S函数来初始化块及其工作向量,调用mdlInitializeSizes,临床条件,mdlCheckParameters,mdlProcessParameters,mdlStart功能。在信号传播常数的情况下,计算输出在模型执行的初始化阶段,从C MEX s -函数调用。

在加速模型执行的时间步进阶段,由输出更新将执行块TLC方法,以及衍生工具零交叉方法(如果存在的话)。的开始块目标文件的方法不用于生成加速模型的代码。

相关的话题