注: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
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
}
需要注意的是,如果不能一行写完,那建议按照如下格式来定义(主要是要将“{”放到第一行的末尾):
第一行: proc+(空格)+函数名+(空格)+{参数}+(空格)+{
中间行: 逻辑运算
最后行: }
全局变量global:
用于将过程中的局部变量变成外界可操作的全局变量
proc hello {} {
global x
set x hi
set i hello}
上述代码,执行结果:
return命令:
proc hello {} {return world}
set i [hello]
return命令没啥好说的,上述代码的结果是,将i变量赋值为world字符串
8>流控制
if 流控制
这个同样建议按照格式来:
第一行: if+(空格)+{表达式}+(空格)+{
中间行: 逻辑运算
第N行: }+(空格)+else+(空格)+{
中间行: 逻辑运算
最后行: }
switch流控制
例子如下,一目了然:
switch 2 {
1 {puts 111}
2 {puts 222}
3 {puts 333}
default {puts xxx}
}
case abcd in a {puts 111} *bc* {puts 333} default {puts xxx}
上述程序对字符串abcd进行判断:
条件一 : 字符串为a
条件二 : 不管字符串的前后字符是啥,只要中间有bc子字符串即可
条件三 : default
9>循环控制
foreach i {0 3 2 1} {
switch $i {
1 {puts 111}
2 {puts 222}
3 {puts 333}
default {puts xxx}
}
}
for {set i 0} {$i < 10} {incr i} {
puts $i
}
初始化i=0,范围 i<10 ,循环i=i+1
set i 10
while {$i!=5} {
puts $i
incr i -1
}
运行的结果,自己就可以想象了
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]
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
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
可以使用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字符串,那么,设计一个程序实现从第三个字符串开始读文件内容:
假如创建了一个过程: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
注:upvar命令(连接变量)
既然说了uplevel那就把upvar命令也说了吧,其类似于uplevel命令,但是其侧重的是在不同层之间连接单一变量
proc hello {a} {upvar $a x ; set x helloworld}
set i hi ; hello i ; puts $i
catch命令
用于阻止因错误而导致的中断执行,类似python中的异常,执行成功返回0,否则返回1
unknown命令
我将这个指令归为异常指令
使用方法:首先定义一个unknown过程,这个过程的参数为cwd(命令)和args(参数)
proc unknown {cwd args} {
puts commend:$cwd
puts args:$args
}
这样的话,当有未知命令或者打错了代码的话,就可以通过unknown过程,控制错误
time命令 time "set i 10"
,该命令将计算执行的时间
trace命令
监视变量的存储的命令,感觉暂时用不到,需要用到的时候再看
命名空间是命令和变量的集合,通过命名空间的封装,来保证他们不会影响其它命名空间的变量和命令
设置新命名空间
首先定义两个hello过程,其中一个在hlf命名空间内,然后测试
namespace eval hlf {pro hello {} {puts hello_hlf}}
pro hello {} {puts hello_all}
设置新变量
直接通过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}
}
目前的理解就是可以在同一命名空间内的不同过程中传递变量,也就不深究了
到此为止算是对TCL的基本使用有了一个大致的理解,里面还有很多具体的函数和函数选项没有涉及到,如果之后用的到的话再做补充吧,但是我觉得,应对VIVADO的TCL的语法,这些基础语法应该足够了的,接下来就是对VIVADO自带的TCL的库里的函数,进行一个了解了
之后遇到不懂的命令,就直接输入命令 -help
,就可以看到一堆帮助了
本文转自: https://blog.csdn.net/long_fly/article/details/78897158