TCL脚本语言
注:TCL在VIVADO中的具体使用将写在VIVADO 之 TCL脚本工具 [下]中,本文只有TCL语法
Tcl(Tool Command Language)是一种很通用的脚本语言,它几乎在所有的平台上都可以解释运行,而且VIVADO也提供了TCL命令行。最近发现TCL脚本貌似比GUI下操作VIVADO效率高一些,方便一些。而且最近跟着官网文档做SDSOC的flatform,发现xilinx官网的文档里都是用TCL命令来完成操作,于是决心学习一下TCL的语法。
应用程序(如VIVADO)使用Tcl作为它的命令语言的好处:
1 Tcl提供了标准语法,一旦用户掌握了Tcl就可以很容易的发布命令给基于Tcl的程序。
2 Tcl实现了很多的功能,使你的工作变得很方便。
3 TCl可作为程序间通信的接口。
命令格式
一条Tcl的命令串包含了多条命令时,用换行符或分号来隔开
而每一条命令包含了一个域的集合,域使用空白分开的,第一个域是一个命令的名字,其它的是作为参数来传给它
数据类型
Tcl只支持一种数据结构:字符串。所有的命令、命令里的所有的参数、命令的结果、变量全部都是都是字符串。
简单实例:
set i 123
将123这个字符串赋值给i变量
unset i
清除变量
set i hi
将hi这个字符串赋值给i变量
set i "hi hello"
hi hello中有空格,所以加引号
set i 123;#开始注释
注意注释前,要先用分号,把命令结束掉,或者换行注释
基本语法和基础命令
在VIVADO中的TCL命令行里,学习这些基本语法
(Windows下 –> 开始 –> 所有程序 –> Xilinx Design Tools –> Vivado xxx –> Vivado xxx Tcl Shell)
1>使用$符号引用变量
其中puts是打印命令
2>使用[]将命令返回值,作为新命令的参数
set j 232命令会返回值232
新命令就成了 set i 232
这里稍微复杂一点点的例子:
set i a[set j b][set k c]
最后的结果就是:j=b ; k=c ; i=abc
3>数组
数组不需要声明,直接赋值即可,也不必按照顺序来:
set i(1) 123 ; set i(16) hi
当然也支持任意维数的数组:
set i(1,2,3) hi
引用的时候直接$i(1,2,3)即可
· parray命令
可以打印出一个数组的全部信息:
· array命令
命令格式:array option arrayName
option 是 操作选项,有如下可选:
name : 返回数组的所有元素的名称
size : 返回数组的长度
startsearch : 初始化一次遍历,返回一个遍历标识符(searchId),这个searchId在下面用到,(是可以多个遍历同时进行的)
下面的命令格式为:array option arrayName searchId
->nextelement : 返回数组中下一个元素,如果没有返回空
->anymore : 如果接下来还有元素,返回1,否则返回0
->donesearch : 结束遍历
4>字符串命令
· string命令
命令格式:string option string1 string2
option 是 操作选项,有如下可选:
compare : 按照字母的排序方式比较,string1 <,=,>string2,分别返回-1,0,1
match : 判断string1和string2是否匹配
first : 检索string2中第一次出现string1的位置,如果没有出现string1则返回-1
last : 和first相反
trim : 从string1中删除开头和结尾的,string2的字符
命令格式:string option string
tolower : 返回string中的所有字符被转换为小写字符后的新字符串
toupper : 返回string中的所有字符串转换为大写后的字符串
trimleft : ,去除string左空白,类似的还有trimright
length : 返回string1的长度
range :
string range abcdef 1 2,返回输出结果为bc
· append命令
字符串追加,可以无限拼接
set i a
append i b c d
puts $i
1
2
3
i变量的值就成了 abcd,注意append i b c d命令,而不是append $i b c d
· split命令
命令格式:split 字符串 分割符,将字符串转换为列表
5>数字操作
tcl中只有string类型的变量,所以当进行数字运算的时候,需要用到incr和expr操作命令
· incr命令
a变量自加-3:incr a -3
a变量自加1 : incr a
· expr命令
类似C语言中的算术操作符有(在Tcl 中的逻辑:真为1,假为0):
!、* 、/、 %、+、-、<<、 >> 、< 、> 、<= 、>= 、== 、!=、& 、^ 、|、&&、 || 、x ? y : z
除此之外,expr还能够识别一些函数及其返回值:
abs(x) 、round(x) 、sin(x)、cos(x) 等
使用方法:expr 表达式
6>list列表
类似python中的列表,比如:{abc {def {jkl ccc}}}是一个有两个元素的列表 abc和{def {jkl ccc}},Tcl中对list的命令有:
(首先set l {abc {def {jkl ccc}}},下面实例中将对这个l列表进行操作)
命令 命令格式 功能 实例
concat concat 列表1 列表2 等 列表拼接
list list 列表1 列表2 等 同上
lindex lindex 列表 索引 索引列表
lrange lrange列表 索引1 索引2 索引列表的一部分
llength llength 列表 返回长度
linsert linsert 列表 索引 对象 列表新增元素
lreplace lreplace 列表 索引1 索引2 对象 把列表对应位置的元素替换
lsearch lsearch (-mode) 列表 对象 在列表中搜索元素的位置(可以加具体mode)
lsort lsort (-mode) 列表 列表中元素排序(可以加具体mode)
join join 列表 (连接符) 将列表中的元素连接成字符串
lappend lappend 列表 对象 向列表追加元素(需要注意如下)
需要注意的是:大部分命令都是对$l进行处理,也把就是l的内容字符串取出来,再处理,并不会对l列表的内容造成影响
需要注意的是lappend命令,lappend $l abcd是无效的,必须lappend l abcd才能实现列表内容的更新,而且是直接更改列表的内容
7>proc自定义函数
proc:
proc hello {str} {
puts hello:$str
}
1
2
3
需要注意的是,如果不能一行写完,那建议按照如下格式来定义(主要是要将“{”放到第一行的末尾):
第一行: proc+(空格)+函数名+(空格)+{参数}+(空格)+{
中间行: 逻辑运算
最后行: }
全局变量global:
用于将过程中的局部变量变成外界可操作的全局变量
proc hello {} {
global x
set x hi
set i hello}
1
2
3
4
上述代码,执行结果:
return命令:
proc hello {} {return world}
set i [hello]
1
2
return命令没啥好说的,上述代码的结果是,将i变量赋值为world字符串
8>流控制
if 流控制
这个同样建议按照格式来:
第一行: if+(空格)+{表达式}+(空格)+{
中间行: 逻辑运算
第N行: }+(空格)+else+(空格)+{
中间行: 逻辑运算
最后行: }
switch流控制
例子如下,一目了然:
switch 2 {
1 {puts 111}
2 {puts 222}
3 {puts 333}
default {puts xxx}
}
1
2
3
4
5
6
case流控制
case abcd in a {puts 111} *bc* {puts 333} default {puts xxx}
1
上述程序对字符串abcd进行判断:
条件一 : 字符串为a
条件二 : 不管字符串的前后字符是啥,只要中间有bc子字符串即可
条件三 : default
9>循环控制
foreach循环:
假如想要将0,3,2,1按照顺序分别放到上述switch的判决条件(列表)里,输出四个结果,那就需要这个foreach了:
foreach i {0 3 2 1} {
switch $i {
1 {puts 111}
2 {puts 222}
3 {puts 333}
default {puts xxx}
}
}
1
2
3
4
5
6
7
8
for循环:
TCL的for循环也是很类似C语言的:
for {set i 0} {$i < 10} {incr i} {
puts $i
}
1
2
3
初始化i=0,范围 i<10 ,循环i=i+1
while循环:
set i 10
while {$i!=5} {
puts $i
incr i -1
}
1
2
3
4
5
运行的结果,自己就可以想象了
10>字符串转为命令
eval命令:
set a set ; set b i ; set c hello ; eval $a $b $c
上述代码就等效于:set i hello
eval将字符串的内容,作为命令,执行
11>打印输出
之前的那个puts命令也是可以打印到命令行,但是,也只是能打印出来而已,而这个format类似于C中的sprintf(用于格式化输出):
format命令:
format可以这样用:
format "%s %d" hello 666
set i [format "%s %d" hello 666]
1
2
scan命令:
说到format,刚好一起把scan说了,这两个命令可以看做是相反的一对, 前者组合成字符串,后者把字符串拆分后赋值给变量
scan 12.34.56.78 %d.%d.%d.%d a b c d
将12.34.56.78拆分,并分别赋值给a b c d四个变量,命令返回赋值成功的变量的个数
puts命令:
puts当然也可以打印到文件中
set f [open test.txt w]
puts -nonewline $f "hello\n"
puts $f "world"
close $f
1
2
3
4
puts -nonewline $f "hello\n"表示的是强制不换行打印,否则自动追加一个换行符
文件系统
基本常用操作:
gets –> 一次读一行文件
puts –> 写入文件
open –> 打开文件
close –> 关闭文件
flush –> 刷新缓冲区
cd命令
和shell中的cd一样
pwd命令
用于查看当前所在的目录
open命令
打开文件,返回文件描述符
命令格式:open 文件名 模式,支持6种模式,和其他编程语言中的文件IO,也是很相似的,模式如下:
r 模式: 打开只读文件(文件必须存在)
r+ 模式: 打开可读写文件[r+和a+模式可以类比]
w 模式: 打开只写文件,若文件存在则清空内容;若文件不存在则创建文件。
a 模式: 以追加方式打开只写文件,若文件不存在,则创建;如果文件存在,则会在文件内容最后面追加写入的数据
xxxx
理论上说open |文件名 模式,在文件名前加个“|”符号,可以以管道的模式打开文件,但是测试一直没有成功,之后用到的话再回来解决吧
xxxx
read命令
set f [open test.txt r]
read $f 6
close $f
1
2
3
可以使用eof命令,判断文件是否读完了,eof $f,读完返回1,否则返回0
上述代码直接从文件中读6个字节;如果想把文件内容全部读出,则直接read $f;如果想一行一行读则使用gets命令:gets $f
source命令
命令格式:source $f
从对应的文件中读出内容,并传给Tcl解释执行
tell命令
返回文件的指针位置,命令格式:tell $f
file命令
命令格式:file option name
option操作选项较多,就直接列个表了,表示如下:
option名称 功能
dirname 返回最后一个“/”之前的部分(目录部分)
tail 返回最后一个“/”以后的部分
executable 返回文件是否可被执行
mkdir 在当前目录下创建文件夹
owned 判断文件是否属于当前目录
exists 返回1 表示文件存在,0 表示文件不存在
extension 返回文件的扩展名
rootname 返回去除文件扩展名后的name
isdirectory 判断是否为目录
isfile 判断是否为文件
writable 判断文件是否可写
readable 判断文件是否可读
size 返回文件的大小
split 把Windows格式的路径拆分
join 组合成标准路径
normalize 返回标准化路径
nativename 返回原生格式路径
pathtype 判断路径为相对路径还是绝对路径
type 返回文件类型(有file、directory、characterSpecial、blockSpecial、fifo、link、socket)
delete 删除指定文件,当需要强制删除非空目录时(命令格式:file delete -force 目录名)
copy 复制文件(命令格式:file copy 源 目标),同样的-force可以强行覆盖同名文件(目标名冲突时)
rename 重命名,同样可以强制
除此之外,file 的 stat 状态操作选项:
命令格式:file stat name k,结果存在数组k里
glob命令
1)查看当前目录下的文件(类似shell中的ls)
glob *
2)查看当前目录下特定后缀的文件
glob *.txt *.tcl
3)查看当前目录下的txt、txl、tcl和tct文件:
glob {*t[xc][tl]}
4)查看当前目录下的子目录里查看txt、txl、tcl和tct文件:
用“\”分割路径,格式为:glob {{目录1,目录2等}\\*.后缀}
5)-type选择查看类型:
命令格式:glob -type {类型1 类型2 等} 目标目录
类型有:
类型 含义
b 块设备
c 字符设备
d 代表目录
f 文件
l 代表符号链接
p 代表命名管道
s 代表套接字
r 读
w 写
x 可执行
seek命令
用于调整文件指针
命令seek $f 2,文件指针定位到序号为2,现在有一个文件名为s1.txt,内容为hello字符串,那么,设计一个程序实现从第三个字符串开始读文件内容:
info命令获取信息
假如创建了一个过程:proc hello { a b c } {puts hi}
执行命令:info args hello,则返回a b c,参数列表
执行命令:info body hello,则返回puts hi,函数体
info procs,返回所有的过程的列表
info procs hello,如果存在hello过程则返回hello字符串,不存在则不返回
info commands,则列出解释器支持的所有命令
info commands create_ip,create_ip是vivado支持的tcl命令,所以这个info返回的值是create_ip,如果不支持该命令的话,则不返回值
info exists kkk,判断kkk变量是否存在
info vars,返回当前变量名的列表
info vars i,如果存在该i变量则返回i字符串,不存在则不返回
info globals,返回全局变量的列表
info globals env,如果存在该env全局变量则返回env,不存在则不返回
info locals,返回local变量列表
info locals i,如果存在该i局部变量则返回i,不存在则不返回
info hostname,返回主机名
info cmdcount,则返回当前解释器已经执行的命令个数
info tclversion,返回解释器版本号
info level,返回当前的在栈中的绝对位置
info level 1,如果加了参数数字,则返回该层的命令和参数
注:uplevel命令(连接参数)
既然说了level那就把uplevel命令说了,level值为0代表顶层,level代表在栈中的绝对位置,过程调用的时候,一层比一层的level值高1,被调用的过程中若想在上一层的环境中执行操作,那么就需要uplevel命令了
proc hello {} { uplevel set a "helloworld" }
set a hi ; hello ; puts $a
1
2
注:upvar命令(连接变量)
既然说了uplevel那就把upvar命令也说了吧,其类似于uplevel命令,但是其侧重的是在不同层之间连接单一变量
proc hello {a} {upvar $a x ; set x helloworld}
set i hi ; hello i ; puts $i
1
2
系统异常、系统监视
catch命令
用于阻止因错误而导致的中断执行,类似python中的异常,执行成功返回0,否则返回1
unknown命令
我将这个指令归为异常指令
使用方法:首先定义一个unknown过程,这个过程的参数为cwd(命令)和args(参数)
proc unknown {cwd args} {
puts commend:$cwd
puts args:$args
}
1
2
3
4
这样的话,当有未知命令或者打错了代码的话,就可以通过unknown过程,控制错误
time命令
time "set i 10",该命令将计算执行的时间
trace命令
监视变量的存储的命令,感觉暂时用不到,需要用到的时候再看
命名空间namespace
命名空间是命令和变量的集合,通过命名空间的封装,来保证他们不会影响其它命名空间的变量和命令
设置新命名空间
首先定义两个hello过程,其中一个在hlf命名空间内,然后测试
namespace eval hlf {pro hello {} {puts hello_hlf}}
pro hello {} {puts hello_all}
1
2
设置新变量
直接通过set hlf::i 888,就可以对hlf空间的i进行设置
删除命名空间
命令:namespace delete hlf
不同命名空间共享变量和过程
通过export和import命令,完成一个命名空间导出过程,另一个命名空间将其导入,完成过程共享
对命名空间的变量进行设置或访问
variable命令,以例子说明:
namespace eval hlf {
variable i 5
proc next {} {variable i;return [incr i]}
proc reset {} {variable i;set i 0}
}
1
2
3
4
5
目前的理解就是可以在同一命名空间内的不同过程中传递变量,也就不深究了
到此为止算是对TCL的基本使用有了一个大致的理解,里面还有很多具体的函数和函数选项没有涉及到,如果之后用的到的话再做补充吧,但是我觉得,应对VIVADO的TCL的语法,这些基础语法应该足够了的,接下来就是对VIVADO自带的TCL的库里的函数,进行一个了解了
之后遇到不懂的命令,就直接输入命令 -help,就可以看到一堆帮助了
————————————————
版权声明:本文为CSDN博主「long_fly」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/long_fly/article/details/78897158