주요 콘텐츠

이 페이지는 기계 번역을 사용하여 번역되었습니다. 영어 원문을 보려면 여기를 클릭하십시오.

래퍼 S-Function 및 TLC 파일 작성

래퍼 개념을 활용하여 Simulink® 및 코드 생성 제품과 원활하게 연동되는 S-Function을 생성하십시오. 다음을 수행할 수 있습니다.

  • MEX S-Function 래퍼(sfunction.mex)를 작성하여 Simulink 모델에 알고리즘을 연동하십시오.

  • TLC S-Function 래퍼(sfunction.tlc)를 생성하여 코드 생성기가 생성된 코드에 사용자의 알고리즘을 삽입하도록 지시하십시오.

MEX S-Function 래퍼

S-Function 래퍼를 사용하여 S-Function을 생성하면, 원본 C/C++ 함수를 거의 또는 전혀 수정하지 않고도 Simulink 모델 및 생성된 코드에 C/C++ 코드 알고리즘을 삽입할 수 있습니다. MEX S-Function 래퍼는 다른 모듈에 있는 코드를 호출하는 S-Function입니다.

참고

MEX S-Function 래퍼는 해당 래퍼를 생성한 MATLAB® 버전에서만 사용하십시오.

my_alg.c 파일에 my_alg라는 알고리즘(즉, C 함수)이 있다고 가정해 봅시다. MEX S-Function 래퍼(예: wrapsfcn.c)를 생성하여 my_alg를 Simulink 모델에 통합할 수 있습니다. 그러면 Simulink 모델은 S-Function 블록에서 my_alg를 호출할 수 있습니다. Simulink S-Function에는 Simulink 엔진이 다양한 API 관련 목적으로 필요로 하는 일련의 빈 함수들이 포함되어 있습니다. 예를 들어, mdlOutputs만이 my_alg를 호출하지만, 이 S-Function 루틴이 아무런 작업도 수행하지 않음에도 불구하고 엔진은 mdlTerminate를 호출합니다.

TLC S-Function 래퍼(예: wrapsfcn.tlc)를 생성하여 생성된 코드에 my_alg 호출을 삽입할 수 있습니다. 비어 있는 함수 호출은 제거할 수 있습니다. mdlOutputs 함수를 실행하는 데 따르는 오버헤드를 피할 수 있으며, 그러면 my_alg 함수를 제거할 수 있습니다.

래퍼 S-Function은 절차적 알고리즘을 만들 때나 레거시 코드를 Simulink 모델에 통합할 때 유용합니다. 다음과 같은 코드를 작성하고 싶다면:

  • 본질적으로 해석적이며(즉, 작동 모드에 따라 파라미터가 크게 달라짐)

  • 대폭 최적화됨 (즉, 코드가 어떤 모드로 작동 중인지 판단하기 위한 불필요한 테스트가 없음)

그렇다면 해당 S-Function에 대해 완전히 인라인 처리된 TLC 파일을 생성해야 합니다.

다음 그림은 래퍼 S-Function의 개념을 보여줍니다.

S-Function 래퍼를 사용하여 알고리즘을 Simulink 모델로 가져온다는 것은, S-Function이 mdlOutputs에서 C/C++ 알고리즘을 호출하는 인터페이스 역할을 한다는 것을 의미합니다. 코드를 수정할 필요 없이 대규모 독립형 C/C++ 프로그램을 모델에 신속하게 통합할 수 있습니다.

이 샘플 모델에는 S-Function 래퍼가 포함되어 있습니다.

wrapsfcn 블록에는 S-Function 래퍼와 알고리즘이 포함된 C/C++ 코드, 이 두 개의 파일이 연관되어 있습니다. 다음은 처음 세 가지 진술입니다:

  1. S-Function의 이름을 지정하십시오(Simulink S-Function 블록 대화 상자에 입력하는 내용).

  2. S-Function이 레벨 2 형식을 사용하도록 지정합니다.

  3. SimStruct 데이터 구조에 대한 접근 권한을 제공합니다. SimStruct는 시뮬레이션 및 코드 생성 과정에서 사용되는 데이터에 대한 포인터를 포함하며, SimStruct에 데이터를 저장하고 해당 데이터에서 데이터를 가져오는 매크로를 정의합니다.

#define S_FUNCTION_NAME wrapsfcn
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"

extern real_T my_alg(real_T u);  /* Declare my_alg as extern */

/*
 * mdlInitializeSizes - initialize the sizes array
 */
static void mdlInitializeSizes(SimStruct *S)
{

    ssSetNumSFcnParams( S, 0); /*number of input arguments*/

    if (!ssSetNumInputPorts(S, 1)) return;
    ssSetInputPortWidth(S, 0, 1);
    ssSetInputPortDirectFeedThrough(S, 0, 1);
    ssSetInputPortRequiredContiguous(S, 0, 1);

    if (!ssSetNumOutputPorts(S,1)) return;
    ssSetOutputPortWidth(S, 0, 1);
    
    ssSetNumSampleTimes( S, 1);
}

/*
 * mdlInitializeSampleTimes - indicate that this S-function runs
 * at the rate of the source (driving block)
 */
static void mdlInitializeSampleTimes(SimStruct *S)
{
    ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
    ssSetOffsetTime(S, 0, 0.0);
} 


/*
 * mdlOutputs - compute the outputs by calling my_alg, which
 * resides in another module, my_alg.c
 */
static void mdlOutputs(SimStruct *S, int_T tid)
{
    const real_T *u = ssGetInputPortRealSignal(S,0);
          real_T *y = ssGetOutputPortRealSignal(S,0);
    *y = my_alg(*u); /* Call my_alg in mdlOutputs */
 }
/*
 * mdlTerminate - called when the simulation is terminated.
 */
static void mdlTerminate(SimStruct *S)
{
}

#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */
#include "simulink.c" /* MEX-file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif

자세한 내용은 Templates for C S-Functions 항목을 참조하십시오.

S-Function 루틴 mdlOutputs에는 my_alg에 대한 함수 호출이 포함되어 있으며, 이는 해당 S-Function이 수행하는 알고리즘을 포함하는 C 함수입니다. my_alg.c의 코드는 다음과 같습니다:

#ifdef MATLAB_MEX_FILE
#include "tmwtypes.h"
#else
#include "rtwtypes.h"
#endif
real_T my_alg(real_T u)
{
return(u * 2.0);
}

자세한 내용은 Manage Build Process File Dependencies 항목을 참조하십시오.

래퍼 S-Function wrapsfcnmy_alg를 호출하여, u * 2.0를 계산합니다. wrapsfcn.mex를 빌드하려면 다음 명령을 사용하십시오:

mex wrapsfcn.c my_alg.c

TLC S-Function 래퍼

TLC S-Function 래퍼는 코드 생성기가 사용자의 코드를 호출하는 방식을 지정하는 TLC 파일입니다. 예를 들어, 생성된 코드의 mdlOutputs 섹션에서 my_alg 호출을 인라인으로 처리할 수 있습니다. MEX S-Function 래퍼 예제에서, my_alg 호출은 다음과 같이 mdlOutputs 섹션에 포함됩니다:

*y = my_alg(*uPtrs[0]);

TLC S-Function 래퍼를 생성할 때의 목표는 생성된 코드에 동일한 유형의 호출을 포함시키는 것입니다.

코드 생성기가 인라인 처리되지 않은 S-Function을 어떻게 실행하는지 살펴보세요. 인라인 처리되지 않은 S-Function은 sfunction.tlc 파일이 없고 sfunction.mex 파일이 있는 것으로 식별됩니다. 인라인 처리되지 않은 S-Function에 대한 코드를 생성할 때, 코드 생성기는 함수 포인터를 통해 mdlOutputs를 호출하는 코드를 생성하며, 이 예제에서는 해당 함수가 my_alg를 호출합니다.

이 래퍼 예제에는 wrapsfcn.mex라는 하나의 S-Function이 포함되어 있습니다. 생성된 코드와 함께 my_alg라는 추가 모듈을 컴파일하고 링크해야 합니다. MATLAB 명령 프롬프트에서 다음을 입력하십시오:

set_param('wrapper/S-Function','SFunctionModules','my_alg')

인라인화 처리되지 않은 S-Function의 코드 오버헤드

grt.tlc를 시스템 타깃 파일로 사용하고 wrapsfcn.tlc를 사용하지 않을 때 생성되는 코드는 다음과 같습니다:

<Generated code comments for wrapper model with noninlined wrapsfcn S-function>

#include <math.h>
#include <string.h>
#include "wrapper.h"
#include "wrapper.prm"

/* Start the model */
void mdlStart(void)
{
  /* (start code not required) */
}

/* Compute block outputs */
void mdlOutputs(int_T tid)
{
  /* Sin Block: <Root>/Sin */
  rtB.Sin = rtP.Sin.Amplitude *
    sin(rtP.Sin.Frequency * ssGetT(rtS) + rtP.Sin.Phase);

  /* Level2 S-Function Block: <Root>/S-Function (wrapsfcn) */
  {
    /* Noninlined S-functions create a SimStruct object and
     * generate a call to S-function routine mdlOutputs
     */
    SimStruct *rts = ssGetSFunction(rtS, 0);
    sfcnOutputs(rts, tid);
  }

  /* Outport Block: <Root>/Out */
  rtY.Out = rtB.S_Function;
}

/* Perform model update */
void mdlUpdate(int_T tid)
{
  /* (update code not required) */
}

/* Terminate function */
void mdlTerminate(void)
{
  /* Level2 S-Function Block: <Root>/S-Function (wrapsfcn) */
  {
/* Noninlined S-functions require a SimStruct object and
     * the call to S-function routine mdlTerminate
     */
    SimStruct *rts = ssGetSFunction(rtS, 0);
    sfcnTerminate(rts);
  }
}

#include "wrapper.reg"

/* [EOF] wrapper.c */

wrapper.reg로 생성된 파일에는 래퍼 S-Function 블록에 대한 SimStruct의 초기화 내용이 포함되어 있습니다. 모델 내의 각 S-Function 블록마다 SimStruct라는 아이가 하나씩 있습니다. S-Function에 대한 TLC 래퍼를 생성하면 이러한 오버헤드를 상당히 줄일 수 있습니다.

래퍼 S-Function 인라인 처리

생성된 코드는 다음 코드를 사용하여 mdlOutputs 내에서 사용자의 S-Function인 wrapsfcn.c를 호출합니다:

SimStruct *rts = ssGetSFunction(rtS, 0);
sfcnOutputs(rts, tid);

이 호출에는 계산적 오버헤드가 수반됩니다. Simulink 엔진은 S-Function 블록에 대해 SimStruct 데이터 구조를 생성합니다. 코드 생성기는 함수 포인터를 통해 mdlOutputs를 실행하는 호출을 생성한 다음, mdlOutputsmy_alg를 호출합니다. my_alg, C/C++ 알고리즘 호출을 인라인 처리하면 SimStruct와 불필요한 함수 호출을 모두 제거할 수 있어, 생성된 코드의 효율성을 높이고 크기를 줄일 수 있습니다.

래퍼 S-Function을 인라인으로 처리하려면 해당 S-Function에 대한 sfunction.tlc 파일이 필요합니다. TLC 파일에는 my_alg 함수 호출이 반드시 포함되어야 합니다. 이 그림은 알고리즘, 래퍼 S-Function, 그리고 sfunction.tlc 파일 간의 관계를 보여줍니다.

my_alg 호출을 인라인으로 처리하려면, 함수 호출을 S-Function과 동일한 이름의 sfunction.tlc 파일에 포함시키십시오(이 예제에서는 wrapsfcn.tlc). Target Language Compiler는 생성된 코드에서 S-Function을 호출하는 기본 방식을 재정의합니다.

이 코드는 wrapsfcn.c를 인라인으로 처리하는 TLC 파일 wrapsfcn.tlc입니다:

%% File    : wrapsfcn.tlc
%% Abstract:
%%      Example inlined tlc file for S-function wrapsfcn.c
%%

%implements "wrapsfcn" "C"

%% Function: BlockTypeSetup ====================================================
%% Abstract:
%%      Create function prototype in model.h as:
%%      "extern real_T my_alg(real_T u);" 
%%
%function BlockTypeSetup(block, system) void
  %openfile buffer
    extern real_T my_alg(real_T u); /* This line is placed in wrapper.h */
  %closefile buffer
  %<LibCacheFunctionPrototype(buffer)>
%endfunction %% BlockTypeSetup

%% Function: Outputs ===========================================================
%% Abstract:
%%      y = my_alg( u );
%%
%function Outputs(block, system) Output
  /* %<Type> Block: %<Name> */
  %assign u = LibBlockInputSignal(0, "", "", 0)
  %assign y = LibBlockOutputSignal(0, "", "", 0)
  %% PROVIDE THE CALLING STATEMENT FOR "algorithm"
  %% The following line is expanded and placed in mdlOutputs within wrapper.c
  %<y> = my_alg(%<u>); 

%endfunction %% Outputs

이 코드의 첫 번째 섹션은 wrapsfcn S-Function 블록을 인라인 처리하고 C 언어로 코드를 생성합니다:

%implements "wrapsfcn" "C"

다음 단계는 모델 내의 모든 wrapsfcn S-Function 블록에 대해, 생성된 wrapper.h 파일에서 my_alg 루틴을 외부(external)로 선언해야 한다는 사실을 코드 생성기에 알리는 것입니다. BlockTypeSetup 함수를 사용하여 모든 wrapsfcn S-Function 블록에 대해 이 선언을 한 번에 수행하십시오. 이 함수에서는 Target Language Compiler가 버퍼를 생성하고, 생성된 헤더 파일 wrapper.hmy_algextern로 캐시하도록 지시합니다.

마지막 단계는 my_alg 함수 호출을 인라인 처리하는 것입니다. Outputs 함수는 호출을 인라인 처리합니다. 이 함수에서는 블록의 입력 및 출력에 접근하고 my_alg를 직접 호출합니다. 이 호출은 wrapper.c에 포함되어 있습니다.

인라인 코드

래퍼 S-Function을 인라인으로 처리할 때 생성되는 코드는 기본 생성 코드와 유사합니다. mdlTerminate 함수에는 빈 함수에 대한 호출이 포함되어 있지 않으며, mdlOutputs 함수는 이제 my_alg를 직접 호출합니다.

void mdlOutputs(int_T tid)
{
  /* Sin Block: <Root>/Sin */
  rtB.Sin = rtP.Sin.Amplitude *
    sin(rtP.Sin.Frequency * ssGetT(rtS) + rtP.Sin.Phase);

  /* S-Function Block: <Root>/S-Function */
  rtB.S_Function = my_alg(rtB.Sin); /* Inlined call to my_alg */

  /* Outport Block: <Root>/Out */
  rtY.Out = rtB.S_Function;
}

wrapper.reg는 S-Function에 대한 자식 함수 SimStruct를 생성하지 않는데, 이는 생성된 코드가 my_alg를 직접 호출하기 때문이며, 이로 인해 1KB 이상의 메모리 사용량을 절감할 수 있습니다.

참고 항목

도움말 항목