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 - 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[] == "" && idNum[] ==
%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 .;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 =[[, "good"]; [3, 4.5F]]
%assign cast = CAST("Real32", %<data[][]>)
%warning %<cast>
>> tlc text.tlc
Warning: 1.0E+0F

变量的存在性

通常为了避免语法错误,在访问记录的某个参数前需要确定它是否真正存在,这是就需要使用EXISTS函数,另外,对于文件的存在性,需要使用FILE_EXISTS(expr)函数。

EXISTS(Var)

%%text.tlc
%createrecord record {foo subrec{foo }}
%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 subrec{foo }}
%if ISFIELD(record, "foo")
%warning record has a parameter foo which value is %<GETFIELD(record, "foo")>.
%<SETFIELD(record, "foo", )>
%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 subrec{foo }}
%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,)>
%warning %<ISEQUAL(,.0F)>
%warning %<ISEQUAL("Str","St")>
>> tlc text.tlc
Warning: 1
Warning: 1
Warning: 0

判断变量类型

使用TYPE函数可以返回一个变量的类型,格式为:

TYPE(expr)

%%text.tlc
%warning %<TYPE("Str")>
%warning %<TYPE([,;3,5])>
%warning %<TYPE(.3F+.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", [:])
%matlab disp(data)
%warning The data type of variable "data" is %<TYPE(data)>.
%warning And the element data type is %<TYPE(data[])>.
>> 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
%assign at = "gsd"
%endif
>> tlc text.tlc -dn
Source: C:\Users\lenovo\Desktop\text.tlc
: %%text.tlc
: %if
: %assign at = "gsd"
: %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 ,
: %% Abstract: Embedded real-time system target file.
: %%
: %selectfile NULL_FILE
:
: %assign CodeFormat = "Embedded-C"
:

使用next命令(或简易命令n)可进行单步执行,step则是step in执行。使用continue或cont可以全速执行,只有遇到断点和错误时停下。断点设置通过break或b实现,并制定断点所在的文件及行数,文件名及行数之间使用冒号":"分隔,如break ert.tlc:14。如果break命令后面的数值对应的行没有代码(全部为空或全部为注释 ),那么TLC调试器将会自动把断点转移到其后最近的有效代码行,并给出warning提醒。

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

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(, " ", lcv, sigIdx)
%assign y = LibBlockOutputSignal(, " ", lcv, sigIdx)
%assign x = LibBlockDWork(dwork, "", lcv, sigIdx)
/* Calculate the filter result */
%<y> = (%<u> - %<x>) * %<t_coef> + %<x>;
%<x> = %<y>;
%endroll %endfunction

最新文章

  1. android6.0的坑
  2. Codeforces CF#628 Education 8 A. Tennis Tournament
  3. 开始研究web,mark一下
  4. Fiddler-1 安装
  5. ABBYY FineReader 12对系统有哪些要求
  6. HDU 1681 Frobenius
  7. LeetCode33 Search in Rotated Sorted Array
  8. WPF串口通信数据采集
  9. cmp比较指令对标志寄存器的影响
  10. 【M12】了解“抛出一个exception”与“传递一个参数”或“调用一个虚函数”之间的差异
  11. js阻止事件冒泡的方法
  12. hpux操作系统的关机与重新启动命令
  13. 新生赛(2) problem 2 丁磊养猪
  14. 关于BSTR和SysStringLen方法的简单研究
  15. 推荐五款Android 应用的自动化测试工具
  16. ci框架学习告一段落,总结一下
  17. Pipeline in scala——给scala添加管道操作
  18. ThreadPoolExecutor系列一——ThreadPoolExecutor 机制
  19. 第二篇:用Android Studio编写Hello World
  20. 关于height、offsetheight、clientheight、scrollheight、innerheight、outerheight的区别一览

热门文章

  1. Linux 账号管理及ACL权限设置,PAM模块简介
  2. [洛谷P3806] [模板] 点分治1
  3. nodejs 中国汉字模糊查询简单(很low)实现
  4. 瑞星发布Linux系统安全报告:Linux病毒或将大面积爆发
  5. sm1、sm2、sm3、sm4简单介绍
  6. textarea 是否换行的问题解决
  7. 转:Zabbix-3.0.x使用OneAlert发送告警
  8. Luogu_2279_[HNOI2003]消防局的设立
  9. C++ 标准IO
  10. IDEA工具java.io.IOException: Could not find resource SqlMapConfig.xml