可编程逻辑
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列表进行操作)
需要注意的是:大部分命令都是对$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流控制
case abcd in a {puts 111} *bc* {puts 333} default {puts xxx}
上述程序对字符串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}
}
}
for循环:
TCL的for循环也是很类似C语言的:
for {set i 0} {$i 《 10} {incr i} {
puts $i
}
初始化i=0,范围 i《10 ,循环i=i+1
while循环:
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操作选项较多,就直接列个表了,表示如下:
除此之外,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
注: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命令
监视变量的存储的命令,感觉暂时用不到,需要用到的时候再看
命名空间namespace
命名空间是命令和变量的集合,通过命名空间的封装,来保证他们不会影响其它命名空间的变量和命令
设置新命名空间
首先定义两个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,就可以看到一堆帮助了
全部0条评论
快来发表一下你的评论吧 !