TLC(Target Language Compiler)是一種為轉換為目標語言而存在的額解釋性語言,其目的就是將模型中編譯出來的rtw文件轉換為目標代碼(C/C++等)。與M語言類似,既可以寫成腳本文件,也能夠作為函數存在,都是解釋性語言,更相似的是它們都提供具有強大功能的內建函數庫。
18.1 TLC的作用
- 支持模型針對通用或特定目標硬件的代碼生成功能;
- 為S函數模塊提供代碼生成功能,可以讓用戶自己增加支持代碼生成的模塊;
- 在代碼生成過程中,生成不依賴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 基本語法
- [text|%<expression>]*
text表示字符串,將原原本本地展開到輸出流中。在%<>之中的是TLC變量,通過%<>作用將變量的執行結果顯示到輸出流中。
- %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搜索路徑依照如下順序:
- 當前路徑;
- 所有的%addincludepath添加的路徑,對於多個%addincludepath,依照從下到上的搜索順序;
- 命令行中通過-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