Simulink仿真入門到精通(十八) TLC語言


TLC(Target Language Compiler)是一種為轉換為目標語言而存在的額解釋性語言,其目的就是將模型中編譯出來的rtw文件轉換為目標代碼(C/C++等)。與M語言類似,既可以寫成腳本文件,也能夠作為函數存在,都是解釋性語言,更相似的是它們都提供具有強大功能的內建函數庫。

18.1 TLC的作用

  1. 支持模型針對通用或特定目標硬件的代碼生成功能;
  2. 為S函數模塊提供代碼生成功能,可以讓用戶自己增加支持代碼生成的模塊;
  3. 在代碼生成過程中,生成不依賴S函數模塊的自定義過程代碼。

Simulink中提供了很多既有TLC文件,如果擅自修改可能導致Simulink Coder功能性錯誤,故Mathworks提倡用戶盡量不要修改TLC。但是如果能熟練掌握TLC的運行機制和編寫方法,不僅不會傷害SImulink的功能,還可以巧妙利用TLC語言實現更多的自動化代碼生成功能。

如matlab目錄下Constant.tlc文件內容如下:

%% 
%% 
%% 
%%
%% Copyright 1994-2015 The MathWorks, Inc.
%%
%% Abstract: Constant block target file

%implements Constant "C"


%% Function: BlockInstanceSetup ================================================
%% Abstract:
%%      Set expression folding compliant
%%
%function BlockInstanceSetup(block,system) void
  %<LibBlockSetIsExpressionCompliant(block)>
%endfunction


%% Function: BlockOutputSignal =================================================
%% Abstract:
%%      Return the appropriate reference to the parameter.  This function *may*
%%      be used by Simulink when optimizing the Block IO data structure.
%%
%function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void
  %switch retType
    %case "Signal"
      %return LibBlockParameter(Value,ucv,lcv,idx)
    %case "SignalAddr"
      %assign idNum = SLibGetReimAndIdx(idx) 
      %if ucv == "" && lcv == "" && idNum[0] == "" && idNum[1] == 0
        %return SLibBlockParameterBaseAddrAsOutputExpr(Value)
      %else
        %return SLibBlockParameterAddrAsOutputExpr(Value, ucv, lcv, idx)
      %endif
      %%START_ASSERT
    %default
      %assign errTxt = "Unsupported return type: %<retType>"
      %<LibBlockReportError(block,errTxt)>
      %%END_ASSERT
  %endswitch
%endfunction

%% [EOF] constant.tlc

18.2 TLC的語法

TLC是一種以單個%打頭的關鍵字為命令,空格之后跟參數的腳本語言,自身包含了流控制語法、內建函數、關鍵字和常用命令。

18.2.1 基本語法

  1. [text|%<expression>]*

    text表示字符串,將原原本本地展開到輸出流中。在%<>之中的是TLC變量,通過%<>作用將變量的執行結果顯示到輸出流中。

  2. %keyword[arguement1,arguement2,...]

    %keyword表示TLC語言中的命令符,[arguement1,arguement2,...]則表示這個命令符所操作的參數。

如:

% assign Str = "Hello World"

%warning、%error、%trace命令可以將其后的變量或字符串的內容輸出。

>> tlc text.tlc
Warning:  Simulink User

>> tlc text.tlc
Error:  Simulink User
Main program:
==> [00] text.tlc:<NONE>(1)

錯誤使用 tlc_new
Error: Errors occurred - aborting


出錯 tlc (line 88)
  tlc_new(varargin{:});

若用%trace命令代替%warning命令顯示信息,只有在執行TLC文件時在最后增加-v或者-v1才能將%trace后的信息顯示出來。

TLC語言有兩個內建宏TLC_TRUE=1和TLC_FALSE=0,在TLC語言的編寫中會經常用到。

18.2.2 常用指令

注釋

單行注釋:雙百分號%%

多行注釋:/% comment %/

變量內容擴展

即通過%<>操作符將其內容擴展到輸出流中。

%%text.tlc
%assign input1 = 3
%assign input2 = 5
%warning %<input1> + %<input2> = %<input1 + input2>
>> tlc text.tlc
Warning:  3 + 5 = 8

注意:%<>不能嵌套使用。

條件分支

%if expression

%elseif expression

%else

%endif

例:

%%text.tlc
%assign var = 2
%if ISEQUAL(var,1)
    %warning evering is OK.
%else
    %warning var should be 1 but now there is something wrong.
%endif
>> tlc text.tlc
Warning:  var should be 1 but now there is something wrong.

在內建函數ISEQUAL中不需要使用%<>。

開關分支

%switch expression

%case expression

%break

%default

%break

%endswitch

每個%case分支后必須跟%break才能起到真正的選擇作用,否則將執行下一個%case語句。

%%text.tlc
%assign data = [1,2,3,4,5]
%switch TYPE(data)
%case "Number"
%warning Type is Number.
%break
%case "String"
%warning Type is String.
%break
%case "Vector"
%warning Type is Vector.
%break
%case "Matrix"
%warning Type is Matrix.
%break
%endswitch
>> tlc text.tlc
Warning:  Type is Vector.

循環

含%foreach、%roll、%for 3種常用方式。

(1)%foreach

%foreach loopIdx = iterNum

xxxxx

%endforeach

上述語句將loopIdx作為循環體句柄變量控制循環進行,從0開始,每次增加1,一直循環到iterMum-1為止。每個%foreach需要使用%endforeach來終止。

在循環體中可以使用%continue終止當前循環進入下一個循環,或者使用%break直接跳出循環。

%%text.tlc
%assign data = [1,2,3,4,5]
%foreach idx = 5
    %if ISEQUAL(idx,1)
        %continue
    %elseif ISEQUAL(idx,4)
        %break
    %endif
    %warning data[%<idx>] = %<data[idx]>
%endforeach
>> tlc text.tlc
Warning:  data[0] = 1
Warning:  data[2] = 3
Warning:  data[3] = 4

(2)%roll

TLC提供%roll這種循環方式主要是為Simulink模塊端口信號相關代碼生成定義時使用的。當模塊的輸入/輸出信號是多維時,需要通過%roll對信號的每一維進行循環,使生成的代碼同在Simulink環境中進行仿真時具有相同的維數。

%roll

%endroll

例:y=2×u

/% roll normally used in block tlc files to roll the 
inport/outport/parameter to access each dimension of them. %/
/* %<Type> Block: %<Name> */
%assign rollVars = ["U", "Y"]
%roll sigIdx = RollRegions, lcv = RollThreshold, block,...
"Roller", rollVars
%assign y = LibBlockOutputSignal(0, "", lcv, sigIdx)
%assign u = LibBlockInputSignal(0, "", lcv, sigIdx)
%<y> = %<u> * 2;
%endroll

%assign rollVars = ["U", "Y", "P"]定義rollVars變量存儲循環體所涉及的模塊要素,U表示輸入端口,Y表示輸出端口,在帶有參數的模塊中,P代表模塊的參數。rollVars將被傳遞給Roller,設置模塊輸入/輸出或參數中每一維roll的結構體。

%roll sigIdx = RollRegions定義了循環體的循環變量sigIdx,RollRegions是自動計算出來的模塊輸入/輸出或參數的維數向量,如20維輸入信號的RollRegions為[0:19],sigIdx按照這個向量進行逐一循環。

lcv = RollThreshold中lcv是循環控制變量(loop control variable),從RollThreshold獲取值,表示當信號維數小於此數值時不生成for循環語句,而逐條生成語句,只有信號或參數的維數大於等於此數值時才生成for循環。TLC全局變量RollThreshold的值可以在Configuration Parameter→Code Generation→Advanced parameters中的Loop unrolling threshold設定,默認值為5。

LibBlockOutputSignal(portIdx, ucv, lcv, sigIdx)函數包含4個參數,portIdx表示模塊輸入端口的索引號(對於使能或觸發端口,可以使用字符串enable和trigger獲取),ucv通常為空,lcv和sigIdx同前面定義。該函數使用TLC庫函數獲取模塊的輸入/輸出的參數中第sigIdx維所對應的變量,再通過%<y> = %<u> * %<k>;進行代碼生成的變量展開。

在循環體的結尾,使用%endroll作為終止符號。

(3)for

%for的使用方法與%foreach基本相同,並且加入了對於是否執行body以外部分進行roll的判斷。

%for ident1 = const-exp1, const-exp2, ident2 = const-exp3

    Code section1

    %body

    Code section2

    %endbody

    Code section3

%endfor

當const-exp2為非零值時,則Code section1/2/3所有代碼就會執行一次,並且ident2會接收const-exp3的值;當const-exp2為0時則僅執行Code section2段,其他段不執行,並且其以ident1為循環變量進行循環,ident2為空值。

%%text.tlc
%for idx = 3, 0, str = "yes"
   %warning OK?
    %body
         %warning Answer is %<str> .
    %endbody
   %warning Over

%endfor
>> tlc text.tlc
Warning:  Answer is  .
Warning:  Answer is  .
Warning:  Answer is  .
%%text.tlc
%for idx = 3, 1, str = "yes"
   %warning OK?
    %body
         %warning Answer is %<str> .
    %endbody
   %warning Over

%endfor
>> tlc text.tlc
Warning:  OK?
Warning:  Answer is yes .
Warning:  Over

文件流

TLC語言使用%openfile創建文件流緩存或打開一個文件,%selectfile選中或激活一個存在的文件流緩存或文件,%closefile關閉一個文件流緩存或文件。

%openfile streamId = "filename.txt" mode {open for writing}

%selectfile streanId {select an open file}

%closefile streamId {close an open file}

%openfile在打開文件時,第2個參數mode可以是a或者w,表示以“追加”或者“重寫”的方式創建文件或緩存塊,默認重寫。當所填的文件名不存在時,則使用StreamId為名創建一個緩存塊buffer進行后續操作。在%openfile和%closefile之間的內容將被寫入到緩存塊buffer中作為變量保存起來,並可以使用%<buffer>將其展開到生成代碼中去,這個buffer就稱為“流”。

%%text.tlc
%openfile buffer = "my_flow_control.txt"
This is the first time flow control is used. 
%closefile buffer
>> tlc text.tlc

StreamID所表示的參數有2個內建流變量:NULL_FILE和STDOUT,分別表示無輸出和使用終端輸出文件流內容。%selectfile同STDOUT聯合使用時,也可以將字符串輸出到MATLAB的Command Window中。

%%text.tlc
%selectfile STDOUT
text to be placed in the 'buffer' variable.
>> tlc text.tlc
text to be placed in the 'buffer' variable.

記錄

TLC記錄相當於M語言或C語言中的結構體類型,但形式不同,是構成rtw文件的基本元素,格式為record_item{Name Value}。Name是字符串格式,按照字母順序進行排列;Value既可以是字符串也可以是數據類型,這個數據可以是scalar、向量或矩陣類型。

(1)創建一個新紀錄

使用%createrecord命令創建一個新紀錄。

%createrecord NEW_RECORD {foo 1;SUB_RECORD {foo 2}}

NEW_RECORD為新創建的紀錄名,{ }內是其所屬的子紀錄。用一層次的子紀錄使用";"隔開,子紀錄再創建嵌套子紀錄時使用record_item {Name Value}方式。

可以通過最上層記錄名訪問其子紀錄內容。

%assign var1 = NEW_RECORD.foo

%assign var2 = NEW_RECORD.SUB_RECORD.foo

也可以創建具有全局訪問權限的紀錄(::表示全局標量標識符)

%createrecord::NEW_RECORD {foo 1;SUB_RECORD {foo 2}}

(2)追加紀錄

使用%addtorecord命令向已經存在的額記錄中追加新的子紀錄。

%addtorecord OLD_RECORD NEW_FIELD_NAME NEW_FIELD_VALUE

3個參數,第一個參數為追加紀錄的對象,第2個和第3個為子紀錄的名字和值。

%%text.tlc
%createrecord NEW_RECORD {foo 1;SUB_RECORD {foo 2}}
%addtorecord NEW_RECORD str "I love Simulink"
%warning %<NEW_RECORD>
>> tlc text.tlc
Warning:  { SUB_RECORD { foo 2 }; foo 1; str "I love Simulink" }

(3)合並紀錄

使用%mergerecord命令合並既存的兩個紀錄。

%mergerecord OLD_RECORD NEW_RECORD

合並后的內容保存在第一個參數中,第二個參數的值不變。

當新舊兩個記錄中同樣層次下存在相同名的紀錄時,則保留這項紀錄各自的值不合並。

%%text.tlc
%createrecord NEW_RECORD {foo 1;SUB_RECORD {foo 2}}
%createrecord NEW_RECORD2 {str "I love Simulink"} %mergerecord NEW_RECORD NEW_RECORD2 %warning NEW_RECORD = %<NEW_RECORD> NEW_RECORD2 = %<NEW_RECORD2> %% another demo
%createrecord NEW_RECORD {foo 1;SUB_RECORD {foo 2}}
%createrecord NEW_RECORD2 {foo 12} %mergerecord NEW_RECORD NEW_RECORD2 %warning NEW_RECORD = %<NEW_RECORD> NEW_RECORD2 = %<NEW_RECORD2>
Warning:  { SUB_RECORD { foo 2 }; foo 1; str "I love Simulink" }
>> tlc text.tlc
Warning:  NEW_RECORD = { SUB_RECORD { foo 2 }; foo 1; str "I love Simulink" } NEW_RECORD2 = { str "I love Simulink" }
Warning:  NEW_RECORD = { SUB_RECORD { foo 2 }; foo 1 } NEW_RECORD2 = { foo 12 }

(4)拷貝紀錄

使用%copyrecord命令進行紀錄拷貝。

%copyrecord NEW_RECORD OLD_RECORD

OLD_RECORD是一個既存的紀錄,NEW_RECORD則是OLD_RECORD的一份拷貝。

%%text.tlc
%createrecord NEW_RECORD {foo 1;SUB_RECORD {foo 2}}
%copyrecord NEW_RECORD2 NEW_RECORD
%warning NEW_RECORD = %<NEW_RECORD> NEW_RECORD2 = %<NEW_RECORD2>
>> tlc text.tlc
Warning:  NEW_RECORD = { SUB_RECORD { foo 2 }; foo 1 } NEW_RECORD2 = { SUB_RECORD { foo 2 }; foo 1 }

(5)刪除紀錄

使用%undef命令刪除記錄中的域或者整個紀錄。

%undef var

var表示一個TLC變量、一個記錄名或一個紀錄中的域的名字。

要刪除一個域成員,可以借助%with進行范圍指定。

%%text.tlc
%createrecord NEW_RECORD {foo 1;SUB_RECORD {foo 2}}
%with NEW_RECORD
%undef foo
%endwith
%warning NEW_RECORD = %<NEW_RECORD> 
>> tlc text.tlc
Warning:  NEW_RECORD = { SUB_RECORD { foo 2 } } 

變量清除

使用%under命令可以刪除TLC變量。

%assign data = [1,2,3,4,5]
%undef data
%foreach idx = 5
    %warning data[%<idx>] = %<data[idx]>
%endforeach

%% error will occur because data is deleted.
>> tlc text.tlc
錯誤使用 tlc_new
Error: File: text.tlc Line: 5 Column: 31
Undefined identifier data
出錯 tlc (line 88)
  tlc_new(varargin{:});

語句換行連接

換行符有兩種,C語言的“\”和M語言的“...”。

%%text.tlc
%createrecord NEW_RECORD1...
    {foo 1;SUB_RECORD {foo 2}}
%warning NEW_RECORD1 = %<NEW_RECORD1> 
%createrecord NEW_RECORD2\
    {foo 1;SUB_RECORD {foo 2}}
%warning NEW_RECORD2 = %<NEW_RECORD2> 
>> tlc text.tlc
Warning:  NEW_RECORD1 = { SUB_RECORD { foo 2 }; foo 1 } 
Warning:  NEW_RECORD2 = { SUB_RECORD { foo 2 }; foo 1 }

訪問范圍

使用%assign str = "I Love Simulink"建立的string型變量str是局部變量,如果定義在TLC腳本中,只有在此腳本內的語句可以訪問;如果是定義在函數里,僅此函數內能訪問該變量;特別地,如果變量定義在for等循環語句塊內部,只有在這個語句塊內部能夠訪問。

%%text.tlc
%assign idx_i = "original string"
%assign data = [[1, "good"];[3, 4.5F]]
%foreach idx_i = 2
    %foreach idx_j = 2
        %warning The element of data[%<idx_i>][%<idx_j>] is ...
%<data[idx_i][idx_j]>
    %endforeach
%endforeach
%warning The original is %<idx_i>
>> tlc text.tlc
Warning:  The element of data[0][0] is 1
Warning:  The element of data[0][1] is good
Warning:  The element of data[1][0] is 3
Warning:  The element of data[1][1] is 4.5E+0F
Warning:  The original is original string

"::"為全局變量標識符。

%assign::str = "I Love Simulink"

輸入文件控制

輸入文件控制包括兩種方式:%include string和%addincludepath string

%include string將在搜索路徑下尋找名為string的文件,並在%include語句出現的地方將此文件的內容內聯展開,類似於C語言中的#include。

%addincludepath string中string是一個絕對路徑或相對路徑,%addincludepath將這個路徑添加到TLC的搜索路徑中,以便出現%include語句時搜索器包含的文件,類似於MATLAB中的addpath命令。

TLC搜索路徑依照如下順序:

  1. 當前路徑;
  2. 所有的%addincludepath添加的路徑,對於多個%addincludepath,依照從下到上的搜索順序;
  3. 命令行中通過-I命令添加的路徑。詳見18.2.6TLC命令行。
%addincludepath "C:\\folder1\\folder2"  %%添加一個絕對路徑
%addincludepath "\\folder2"  %%添加一個相對路徑

輸出格式控制

使用%realformat命令控制輸出實變量所顯示的格式。

如使用16位精度的指數顯示。

%realformat "EXPONENTIAL"

或者無精度損失和最小字符數格式(為S函數提供生成代碼功能的模塊級TLC文件就使用這種格式)

%realformat "CONCISE"

例:

%%text.tlc
%createrecord NEW_RECORD {foo 100.0;SUB_RECORD {foo 2}}
%realformat "EXPONENTIAL"
%warning %<NEW_RECORD.foo>
%realformat "CONCISE"
%warning %<NEW_RECORD.foo>
>> tlc text.tlc
Warning:  1.0E+2
Warning:  100.0

指定模塊生成代碼的語言類型

使用%language命令指定該模塊生成代碼的語種。如果自定義支持嵌入式代碼生成的模塊及其TLC文件,那么在TLC文件中就可以使用%language C來指定語言類型為C語言。

對於Simulink模塊的TLC文件,其中必須包含%implements指令。

%implements "Block-Type" "Language"

Block-Type是指模塊的S函數名,TLC文件也是此名字。Language表示生成的目標代碼的語言類型。

對於自定義的為了生成嵌入式C語言的MCU芯片驅動中斷控制器模塊,其TLC文件開頭必須包含這樣一個命令:

%implements Interrupt "C"

緊接此句的是%function的函數定義,實現S函數代碼生成的具體功能。

另外,%generatefile提供了一個匹配關系,將Simulink模塊和TLC文件聯系起來。

%generatefile "Type" "blockwise.tlc"

Type為rtw文件中模塊的記錄中Type參數的值,也即模塊的blocktype屬性,如:%generatefile "Sin" "sin_wave.tlc"。

斷言

使用%assert命令為TLC斷言。

%assert expression

%%text.tlc
%assert TLC_FALSE
>> tlc text.tlc -da
錯誤使用 tlc_new
Error: File: text.tlc Line: 2 Column: 1
Assertion failed

-da表示使TLC執行%assert命令。

當使用rtwbuild()命令或Ctrl+B啟動模型代碼生成時,為了開啟TLC的斷言功能,必須在Configuration Parameter中勾選Enable TLC assertion。

當expression結果為TLC_FALSE時,TLC將進行堆棧追蹤。

函數

使用%function為開頭定義函數,以%endfunction為終止符結束函數體。

%function name(optional-arguments) void

%return

%endfunction

%function GoodBad(Message) void
%warning Good or Bad %<Message>?!
%endfunction
%%text.tlc
%warning GoodBad(OK)
>> tlc text.tlc
Warning:  GoodBad(OK)

18.2.3 變量類型

TLC語言使用的變量類型和MATLAB變量所使用的內建類型有所不同。在TLC語言中不僅是數據的類型,甚至數據的組織方式都被作為一個單獨的類型,如Matrix、Vector。Range等。

數據類型名(簡寫) 表現形式舉例 說明
Boolean(B) 0==0 返回值為TLC_TRUE或TLC_FALSE,由邏輯操作返回
Number(N) 100 整型數
Real(D)s 3.14159 浮點數
Real32(F)s 3.14159F 32位浮點數
Complex(C) 1.0+2.0i 64位雙精度浮點數
Complex32(C32) 10.F+2.0Fi 32位單精度浮點數
Gaussion(G) 1+2i 32位整型復數
Unsigned(U) 100U 32位無符號整數
Unsigned Gaussion(UG) 1U+2Ui 32位無符號整型復數
String "I Love MATLAB" 包含在雙引號中的字符串
Identifier abc 此類型僅出現在rtw記錄中,不可直接在TLC表達式中使用。如果希望比較其內容,可直接與String類型比較,Identifer將自動轉換為String類型
File

%openfile out="xx.c"

%openfile buffer

使用%openfile打開的字符串緩沖或文件
Function %function my_func ... TLC的函數類型,需要使用%endfunction結束
Range [1:10] 表示一組整數序列,如RollRegions使用在Roll循環體中表示循環變量遍歷的值
Vector [1,2.0F,"good"] 向量類型,每個元素可以是TLC的內建類型,但不能是Vector或Matrix
Matrix %assign a=[[1,"good"];[3,4.5F]] 矩陣類型,矩陣中每個元素類型可以不同,但是不能為Vector或Matrix
Scope system {...} 范圍類型,如rtw中的CompiledModel、block記錄的范圍
Subsystem <sub> 子系統標示符,作為語句擴展的內容
Special FILE_EXISTS 特殊內建類型如FILE_EXISTS

18.2.4 操作符和表達式

操作符和表達式 作用說明
::variable 全局變量,當出現在函數中時,告訴函數該變量的訪問權限是全局的,不適用函數的局部訪問權限
expr[indx] 數組或矩陣的下標索引訪問方式,indx必須是0~(N-1),N為數組長度或矩陣某一維的長度
func([expr[,expr]...]) 函數調用,func為函數名,expr為函數的參數列表
expr.expr 域訪問符,第一個expr為紀錄類型,第2個expr為其內部的一個參數名
(expr) 括號,括號內的表達式優先度高
!expr 邏輯取反,expr必須是Bollean或數值類型,返回值為TLC_TURE或TLC_FALSE
-expr 一元操作符,取負,expr必須為數值類型
+expr 一元操作符,取正,expr必須為數值類型,一般表達式默認為,不必寫出
~expr 按位取反,expr必須為整數
expr*expr 乘法運算符,expr必須為數值類型
expr/expr 除法運算符,expr必須為數值類型
expr+expr

+運算符,可以用於scalar、vector、matrix和record等多種數據類型,且作用不同

用於scalar數值時表示兩個數相加,此時expr必須為數值類型

用於string時,將兩個字符串拼接起來返回

當首個expr是vector,第二個expr是數值類型時,將第二個數追加到vector中

當首個expr是matrix,第二個expr是vector時,如果vector與matrix的列數相同,則追加到matrix中作為新的一行元素

如果首個expr是記錄類型,則第二個expr作為一個參數追加到這個記錄類型中去,其值為第二個expr的當前值

expr-expr 減法運算符,expr必須是數值類型
expr%expr 求余運算符,expr必須是整數類型
expr<<expr2 左移操作符,將expr按照二進制位向左移動expr2位
expr>>expr2 右移操作符,將expr按照二進制位向右移動expr2位。>>不能直接在%<>中被識別,需要使用\進行轉義,如%<expr\>>expr2>
expr<expr2 比較expr是否小於expr2,expr和expr2都必須是數值類型
expr>expr2 比較expr是否大於expr2,expr和expr2都必須是數值類型,在%<>中時需要進行轉義,如%<expr\>expr2>
expr<=expr2 比較expr是否小於等於expr2,expr和expr2都必須是數值類型
expr>=expr2 比較expr是否大於等於expr2,expr和expr2都必須是數值類型,在%<>中時需要進行轉義,如%<expr\>=expr2>
expr==expr2 比較expr是否等於expr2
expr!=expr2 比較expr是否不等於expr2
expr&expr2 將兩個操作數進行二進制按位與操作,expr和expr2必須是整數類型
expr|expr2 將兩個操作數進行二進制按位或操作,expr和expr2必須是整數類型
expr^expr2 將兩個操作數進行二進制按位異或操作,expr和expr2必須是整數類型
expr&&expr2 將兩個操作數進行按邏輯與操作,返回值為TLC_TRUE或TLC_FALSE,expr和expr2可以是數值類型或Boolean類型
expr||expr2 將兩個操作數進行按邏輯或操作,返回值為TLC_TRUE或TLC_FALSE,expr和expr2可以是數值類型或Boolean類型
expr?expr1:expr2 當expr值為TLC_TRUE時返回expr1,否則返回expr2
expr1,expr2 逗號分隔符,返回后面的變量expr2,多個逗號分開時返回最后一個變量

注意:沒有冪運算符。

18.2.5 TLC內建函數

TLC提供的內建函數全部使用大寫字母書寫。

數據類型轉換

CAST函數是TLC語言中負責數據類型轉換的重要函數。

CAST("DataType", variablename)

第1個參數表示第2個參數轉換的目標類型名,如"Number"、"Real32"、"String"等,第2個操作數則是進行轉換的目標操作數。

%%text.tlc
%assign data =[[1, "good"]; [3, 4.5F]]
%assign cast = CAST("Real32", %<data[0][0]>)
%warning %<cast>
>> tlc text.tlc
Warning:  1.0E+0F

變量的存在性

通常為了避免語法錯誤,在訪問記錄的某個參數前需要確定它是否真正存在,這是就需要使用EXISTS函數,另外,對於文件的存在性,需要使用FILE_EXISTS(expr)函數。

EXISTS(Var)

%%text.tlc
%createrecord record {foo 1 subrec{foo 2}} %if EXISTS(record.foo) %warning record has a parameter which value is %<record.foo>. %endif %if FILE_EXISTS("text.tlc") %warning text.tlc is on the path. %endif
>> tlc text.tlc
Warning:  record has a parameter which value is 1.
Warning:  text.tlc is on the path.

記錄的域操作

ISFIELD:判斷某個字符串表示的參數名是否是記錄的域;

GETFIELD:獲取記錄中參數的值;

SETFIELD:設置記錄中參數的值;

REMOVEFIELD;刪除記錄中某參數。

%%text.tlc
%createrecord record {foo 1 subrec{foo 2}}
%if ISFIELD(record, "foo")
%warning record has a parameter foo which value is %<GETFIELD(record, "foo")>.
%<SETFIELD(record, "foo", 12)>
%warning parameter foo value is %<GETFIELD(record, "foo")> now.
%<REMOVEFIELD(record, "foo")>
%warning parameter foo of record now is existed? Ans: %<ISFIELD(record, "foo")>
%endif
>> tlc text.tlc
Warning:  record has a parameter foo which value is 1.
Warning:  parameter foo value is 12 now.
Warning:  parameter foo of record now is existed? Ans: 0

除了上述4個函數之外,還有FIELDNAMES函數可以查詢記錄中包含的內一層的紀錄名。

%%text.tlc
%createrecord record {foo 1 subrec{foo 2}}
%warning %<FIELDNAMES(record)>
>> tlc text.tlc
Warning:  [foo, subrec]

相等判斷

使用ISEQUAL函數可以判斷2個變量是否相等。

ISEQUAL(expr1, expr2)

當expr1、expr2都為數值類型時,即使不是同一個數據類型,只要表達式運算的值大小相等,都會返回TLC_TRUE,否則才返回TLC_FALSE。如果expr1和expr2不是數據類型,二者的變量類型和內容必須完全一樣時才會返回TLC_TRUE。

%%text.tlc
%warning %<ISEQUAL(TLC_TRUE,1)>
%warning %<ISEQUAL(3,3.0F)>
%warning %<ISEQUAL("Str","St")> 
>> tlc text.tlc
Warning:  1
Warning:  1
Warning:  0 

判斷變量類型

使用TYPE函數可以返回一個變量的類型,格式為:

TYPE(expr)

%%text.tlc
%warning %<TYPE("Str")>
%warning %<TYPE([1,2;3,5])>
%warning %<TYPE(10.3F+6.71Fi)> 
>> tlc text.tlc
Warning:  String
Warning:  Matrix
Warning:  Complex32 

判斷空格

判斷空格與判斷空值不同,當一個變量內僅僅包含空格,如\n、\t、\r等時返回1,否則返回零。

WHITE_SPACE函數就是判斷參數是否為全空格的函數。

WHITE_SPACE(expr)

%%text.tlc
%warning %<WHITE_SPACE("\t\n\r\r")>
>> tlc text.tlc
Warning:  1調用

matlab函數

TLC本身雖然沒有MATLAB那么強大的函數庫,卻可以方便地調用MATLAB的內建函數。調用沒有返回值的MATLAB函數時使用%matlab命令,調用有返回值的函數時則使用FEVAL命令,對於有多個返回值的函數,TLC只能接受首個返回值。

%matlab fun(agr)

%%text.tlc
%matlab disp("TLC calls MATLAB function.")
>> tlc text.tlc
TLC calls MATLAB function.

%assign result = FEVAL("MATLAB-function-name", rhs1, rhs2, ..., rhs3, ...)

FEVAL函數的首個參數為MATLAB函數名,用雙引號括起來,其后參數為這個MATLAB函數的參數列表,返回值只能接收MATLAB函數的首個返回值,且其數據類型自動轉換為TLC的內建數據類型。

%%text.tlc
%assign data = FEVAL("sin", [0:9])
%matlab disp(data)
%warning The data type of variable "data" is %<TYPE(data)>. 
%warning And the element data type is %<TYPE(data[0])>.
>> tlc text.tlc
         0    0.8415    0.9093    0.1411   -0.7568   -0.9589   -0.2794    0.6570    0.9894    0.4121

Warning:  The data type of variable "data" is Vector. 
Warning:  And the element data type is Real.

又:

%%text.tlc
%realformat "CONCISE"
%assign pos = FEVAL("regexp", "I love Simulink", "Simu")
%assign pos = CAST("Number", pos)
%warning The index of Simu is %<pos>.
>> tlc text.tlc
Warning:  The index of Simu is 8.

18.2.6 TLC命令行

tlc[switch1 expr1 switch2 expr2 ...] filename.tlc

switchx exprx這樣的開關命令可以有多個,順序隨意,switchx表示開關符號,exprx表示開關的參數。如果同一個開關符號出現多次,那么最后一個是有效的。

開關 意義
-r filename 讀取數據文件載入到TLC中,如rtw文件
-v[number] 設置輸出信息的詳細級別為number,不設置時默認級別為1
-Ipath 增加特定文件夾路徑到TLC搜索路徑中
-Opath 指定輸出文件的路徑,輸出文件包括%openfile和%closefile生成的流文件或者日志文件
-m[number] 指定最大的操作數為number,不設置時,默認報出前5個錯誤,如果只寫-m而不寫number,則認為number為1
-x0 僅解析TLC文件而不執行
-lint 進行一些簡單的性能檢查
-p[number] 設置TLC每執行number個操作輸出一個
-d[a|c|f|n|o]

啟動TLC的debug模式

-da使TLC執行%assert命令

-dc啟動TLC命令行調試器

-df filename將啟動TLC調試器並運行名為filename的tlc調試腳本文件。所謂調試腳本文件,就是包含調試命令的文本文件。TLC僅在當前路徑下搜索有效腳本文件

-dn將為所執行的tlc文件產生行覆蓋度日志,告知哪一行被執行了,哪一行沒有執行

-do則停止調試行為

-dr 檢查是否存在環形文件,這種文件彼此相互引用,會造成內存泄漏
-a[ident]=expr 為一個變量ident設置一個初始值expr,與%assign命令功能相同
-shadow[0|1]

設置是否開啟遮蔽警告功能,當記錄中的參數覆蓋了一個局部變量時:

-shadow0關閉警告

-shadow1開啟警告

%%text.tlc
%if 0
%assign at = "gsd"
%endif
>> tlc text.tlc -dn
Source: C:\Users\lenovo\Desktop\text.tlc
     0: %%text.tlc
     1: %if 0
     0: %assign at = "gsd"
     0: %endif

又:untitled.rtw

CompiledModel {
  Name              "untitled"
  OrigName          "untitled"
  Version          "8.5 (R2013b) 08-Aug-2013"
  SimulinkVersion      "8.2"
  ModelVersion          "1.0"
%%text.tlc
%warning CompiledModel.name = %<GETFIELD(CompiledModel, "Name")>
>> tlc text.tlc -r untitled.rtw -v
Warning: File: text.tlc Line: 2 Column: 9
%warning directive:  CompiledModel.name = untitled

18.2.7 TLC調試方法

在Configuration Parameter中開啟Start TLC debugger when generating code,才能夠在生成模型的代碼時進入到調試模式。

在按下Ctrl+B后在Command Window上動態顯示編譯信息,便進入了TLC調試模式。

使用whos命令查看當前訪問范圍內存在的變量及變量的TLC數據類型。

使用print命令查看某個變量的值,在print命令后也可以使用TLC內建函數。

TLC-DEBUG> print BlockCommentType
BlockPathComment

TLC-DEBUG> print TYPE(CombineOutputUpdateFcns)
Number

list命令后面可以跟兩個整數,如list 10,20,意義是顯示當前TLC文件的第10~20行代碼。

TLC-DEBUG> list 10,15
00010: %% Abstract: Embedded real-time system target file.
00011: %%
00012: %selectfile NULL_FILE
00013: 
00014: %assign CodeFormat = "Embedded-C"
00015: 

使用next命令(或簡易命令n)可進行單步執行,step則是step in執行。使用continue或cont可以全速執行,只有遇到斷點和錯誤時停下。斷點設置通過break或b實現,並制定斷點所在的文件及行數,文件名及行數之間使用冒號":"分隔,如break ert.tlc:14。如果break命令后面的數值對應的行沒有代碼(全部為空或全部為注釋 ),那么TLC調試器將會自動把斷點轉移到其后最近的有效代碼行,並給出warning提醒。

TLC-DEBUG> break ert.tlc:13
Warning: File: Debugger Command Line Line: 1 Column: 1
Breakpoint number 1 was set on the next valid line (14)

TLC-DEBUG> cont

Breakpoint 1
00014: %assign CodeFormat = "Embedded-C"

每次斷點設置后,運行到斷點處會停下來,斷點序號就被顯示出來,這個序號作為斷點的標示符,可以用clear命令清除,如clear 1,clear all可以清除已經設置的全部斷點,或者編譯模型生成代碼全過程執行完畢之后,斷點也會自動清除。

使用Disable可以關閉斷點,使用Enable再次打開。

在調試模式下,可以省略%直接使用assign賦值,其他TLC命令不能在調試模式下使用。

TLC-DEBUG> assign str = "Let me try TLC"

TLC-DEBUG> print str
Let me try TLC

調試結束后,可以輸入cont運行代碼生成流程,或者quit命令退出調試模式。

其他的調試命令還有:Condition、up、down、finish、ignore、loadstate、savestate、stop、thread、threads和where等,使用helpcommand可以查詢命令的幫助。

TLC-DEBUG> help finish
  finish - Break after completing the current function
    Continues execution from where it is stopped, and re-enters the debugger after
    the current function has exited, or some other reason to enter the debugger
    (e.g., a breakpoint or error) is encountered, whichever comes first.

18.2.8 TLC文件的覆蓋度

勾選該項后,代碼生成過程中,TLC編譯器會為每個被執行的TLC文件生成一個log文件,存放在model_ert_rtw文件夾中。

這些log文件對TLC的每行語句在代碼生成過程中的執行次數做統計,0表示沒有被執行,1則表示執行一次。

 

根據log文件,開發者能夠發現那些分支語句沒有被執行過,並根據這個分支語句的判斷條件進行新的測試事件的設置,重新執行並分析覆蓋度,不斷改進,使tlc文件編寫得更加可靠高效。

注意:TLC編譯器認為下列命令是不執行的語句,其執行次數都是0,也不產生時間消耗。

%filescope %else %endif %endforeach %endfor %endroll %endwith %body %endbody %endfunction %endswitch %default

Comment: %% or /% text %/

18.2.9 TLC Profiler

TLC代碼的執行時間取決於TLC腳本、宏、函數和內建函數這些構成TLC代碼的元素的執行時間。

勾選該選項,TLC Profiler可以再執行過程中收集TLC代碼各個元素的執行時間,並匯總到HTML的報告中,讓開發者更容易分析找到代碼生成過程中最話費時間的代碼。

18.3 為S函數編寫TLC文件

18.3.1 支持代碼生成的S函數

只有C MEX S函數和Level2 M S函數才支持代碼生成功能,並且這個功能要求S函數有配套的TLC文件。

帶有參數的S函數首先從GUI上獲取用戶的輸入作為參數,通過C語言的宏將GUI控件上的值讀入S函數,再通過S函數的子方法mdlRTW將參數值寫入到模型的rtw文件中,使得TLC 文件能夠獲取這些參數的值,最終展開到生成代碼中合適的位置中去。

C MEX S函數獲取GUI參數的宏

(1)獲取Edit中的數值

#define PARAM(S)(mxGetScalar(ssGetSFcnParam(S,PARAM_INDEX)))

PARAM_INDEX為Edit等控件的參數在GUI控件中的索引號,首先通過ssGetSFcnParam宏函數獲取指向Edit控件中參數的數值,再使用mxGetScalar宏函數獲取指針指向地址的數值。

(2)獲取Edit中的數組

#define PARAM(S) mxGetNumberOfElements(ssGetSFcnParam(S,PARAM_INDEX))

(3)在Edit中獲取字符串

#define PARAM(S)(ssGetSFcnParam(S,PARAM_INDEX))

(4)獲取Popup/radiobutton所選項目的字符串

#define PARAM(S)(mxArrayToString(ssGetSFcnParam(S,PARAM_INDEX)))

(5)獲取Popup/radiobutton所選項目的索引號

(6)獲取Check-box的值

C MEX S函數的mdlRTW函數

mdlRTW函數是專為支持代碼生成的S函數設計的,不支持僅用於仿真的C MEX S函數,其功能為傳遞S函數的參數數據到rtw文件(這些參數必須被設置為not tunable)。它必須被包含在以下 這個預處理語句中。

#if defined(MATLAB_MEX_FILE)

#endif

則函數定義體呈現為:

#ifdefined(MATLAB_MEX_FILE)

#define MDL_RTW

static viod mdlRTW(SimStruct *S)

{

}

18.3.2 模塊TLC文件的構成

子函數 輸出 功能說明
BlockInstanceSetup(block,  system) 不產生輸出 同種類的模塊存在多個時,每一模塊都會執行一次此函數,可以將模塊共同的操作或特例的操作寫入此函數中
BlockTypeSetup(block, system) 不產生輸出 同類模塊即使存在多個也只執行此函數一次,可以將同一類模塊共同地且執行一次的操作寫入此函數中,也可以不實現此函數
Enable(block, system) 產生輸出 為模塊中非虛擬子系統創建Enable函數,並將使能某功能的代碼生成在該函數中
Disable(block, system) 產生輸出 為模塊中非虛擬子系統創建Disable函數,並將禁止某功能的代碼生成在該函數中
Start(block, system) 產生輸出 為模塊中僅執行一次的函數,內部代碼會生成到model_initialize()函數中,通常將模型各變量、狀態或硬件外設初始化的代碼寫在此函數中,因為他們不需要重復執行
InitializeCondition(block, system) 產生輸出 此函數里的代碼通常也用於初始化某個子系統的狀態變量,但是它不一定僅執行一次,而是在當前模塊所在的子系統每次被使能時都會執行
Outputs(block, system) 產生輸出 用於編寫模塊計算輸出的代碼,並將其生成到model_step()函數中
Update(block, system) 產生輸出 用於編寫每個步長更新模塊狀態變量的代碼,其內容生成到model_update()中
Derivatives(block, system) 產生輸出 用於計算模塊連續變量的函數,其內容生成到model_Derivatives()函數中
Terminate(block, system) 產生輸出 此函數用於自定義代碼用,可以存儲數據,釋放內存,復位硬件寄存器等操作,此函數內的代碼將生成到MdlTerminate()中

18.3.3 模塊TLC函數實例

以自定義濾波器為例(第10章)。

%implements sfun_c_filter "C"

%% Function: blockTypeSetup ==========================================================
%%
%% Purpose:
%%      Add some macro defines .
%%
%function BlockTypeSetup(block, system) void

%endfunction


%% Function: Start ==========================================================
%%
%% Purpose:
%%   
%%     these code will appear at model.c initialization function
%%
%function Start(block, system) Output
    /* If need user can add custom initialize code here */
%endfunction


%% Function: Outputs ==========================================================
%%
%% Purpose:
%%   
%%     these code will appear at model.c step function
%%
%function Outputs(block, system) Output

  %assign t_coef = SFcnParamSettings.r_coef
    %assign rollVars = ["U", "Y","DWork"]
    %roll sigIdx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars
        %assign u = LibBlockInputSignal(0, " ", lcv, sigIdx)
        %assign y = LibBlockOutputSignal(0, " ", lcv, sigIdx)
        %assign x = LibBlockDWork(dwork, "", lcv, sigIdx)
            /* Calculate the filter result */
            %<y> = (%<u> - %<x>) * %<t_coef> + %<x>;
            %<x> = %<y>;
    %endroll

%endfunction


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM