学习一样东西,很重要一点就是要知道学这个是为了什么。Tcl作为一种脚本语言,类似于Python,甚至可以用Tcl来简单地处理一些文件,只是不像Python有那么多库的支持。我也是昨天刚开始学习Tcl的语法,目的是为了用Tcl脚本重建Vivado工程,然后便于做版本管理。Tcl确实和很多人说的一样比较容易学,而且我觉得所有做FPGA开发的人应该都先学Tcl。

学习方式

  学习一种语法我觉得最好的方法就是边看边自己敲代码练习,就像RUNOOB上的教程一样,每一条知识点都有一个例子,而且可以在线编辑。学习Tcl也可以采用类似的方法。我比较推崇使用VS Code,下面是我的学习方式。

下载安装

  Active Tcl类似于Tcl的一个发行版,在Tcl的官网上能够找到Tcl解释器的源码,Active Tcl在这个基础上又加了一些常用的Package。从官网下载之后再安装一下就可以了。安装的时候,

  1. 把Tcl的路径添加到PATH环境是必要的!
  2. 不建议把后缀为“.tcl”的文件和它绑定。就是下面图片里的第二个勾最好不要打,因为它会默认让Tcl脚本用Wish Application执行,这不是我期望的,因为我的Tcl脚本只是用在Vivado工程里的。

VS Code配置

  VS Code的安装就不多说了,如果想用VS Code便捷地执行Tcl脚本,可以安装“Code Runner”这个插件,除了这个插件,还有Tcl语法高亮的插件也可以装一个,这个就不细说了。

  Code Runner安装完成后,对插件进行设置,这个插件本身不能识别Tcl脚本语言,所以要从文件的后缀来映射对应的Tcl解释器。在插件的设置中,点击下面红框里的“Edit in settings.json”。

  然后就会打开相应的文件,在"code-runner.executorMapByFileExtension"里加上下面这个设置就好了。这个设置表示,当文件后缀为".tcl"时,运行代码会首先将目录切换到当前文件所在目录,然后调用tclsh执行脚本。tclsh的路径已经在安装的时候添加到PATH环境变量中,所以在这里可以直接运行。

1
".tcl": "cd $dir && tclsh $fileName"

  运行代码的快捷方式是“Ctrl+Alt+N”,也可以按右上角的小三角形来运行,下面是实际使用的效果。

主要知识点

  Tcl的官网教程非常精炼,基本上一天就能把这些内容看完。可以一边对着官网的这个教程,一边在VS Code中自己写,这样的学习是最有效的。Tcler’s Wiki可以搜到几乎与Tcl相关的任何问题。我在这里记录一些Vivado工程中主要用到的语法规则,其他的比如数值计算、循环结构、变量作用域等就不写了,需要用到的时候可以再去查阅。

puts - 输出

  在控制台输出字符串。带空格的字符串可以用花括号({})或者双引号(“”)组合起来。

1
2
3
puts hello;                 # hello
puts {hello world}; # hello world
puts "hello world"; # hello world

set - 设置变量

  set可以设置变量,变量调用需要在前面加“$”,双引号内的“$”开头的变量会被替换,而花括号内的不会。

1
2
3
set var "hello world"
puts "$var"; # hello world
puts {$var}; # $var

  再看下面这个例子会发现,变量在双引号里面,但是也被正确替换了。而且结果输出的是correct。查看if相关的说明,我的理解是if指令有两个阶段,一个是变量替换阶段,一个是命令执行阶段。双引号内的变量在变量替换阶段被替换,而花括号内的变量则是在命令执行阶段被替换。对于if结构的判断条件,文档中建议用花括号。
  其他说明:一条命令需要跨行可以用反斜杠"\",花括号内的内容被当做是连续的,不用反斜杠;“eq”用来比较字符串是否相等的。

1
2
3
4
5
6
if {$var eq "hello world"} {
puts correct
} \
else {
puts error
}

list - 列表

  list和变量没有本质区别,都是字符串。list里的不同列表项是靠空格隔开的,没有空格的变量就是长度为1的list。list相关命令汇总,尖括号表示必须的参数,方括号表示可选的参数:

命令 用法 返回值 功能
list list [arg1] [arg2]…[argn] 列表 将多个变量组合成一个list
split split <string> [splitChars] 列表 将string按照分隔符分割获得列表,默认分隔符是空格
lindex lindex <list> <index> 列表项 返回list对应index的列表项,index从0开始
llength llength <list> 整数 返回list的长度
foreach foreach <varname> <list> <body> / 遍历list中的每一个列表项目,并执行body中的内容

  用下面这个例子可以对list的一些基本操作有一个直观的认识。

1
2
3
4
5
6
7
8
set var "hello world"
set lst [list $var b c]
puts $lst; # {hello world} b c
puts [llength $lst]; # 3
puts [lindex $lst 0]; # hello world
foreach item $lst {
puts $item; # hello world\ b\ c
}

  方括号可以用来获取命令的返回值。例如上面第二行,list命令返回一个列表,传到外面后,用set命令来给这个列表命名为“lst”。如果想在每一行加注释,则需要在这一行命令的结尾加上分号,然后再用“#”加注释。
  list还有很多相关的命令,比如concat,lappend,linsert,lreplace,lset,lsearch,lsort,lrange,增删查改的功能都非常完备,具体的还是看官方教程。

string - 字符串

  string是Tcl最基础的数据结构,在Vivado工程里,经常需要用到字符串比较来检验某个设置是否已经存在。

命令 用法 返回值 功能
string compare string compare [-nocase] [-length int] <string1> <string2> -1/0/1 根据字符的ASCII码,逐个比较字符,string1比string2小,返回-1;相等返回0;string1比string2大,返回1; -nocase可以忽略大小写;-length可以之比较前面的int个字符
string equal string compare [-nocase] [-length int] <string1> <string2> 0/1 string1和string2相等,则返回1,不相等返回0; -nocase和-length的功能同上
string match string match [-nocase] pattern string 0/1 string和如果能匹配模版pattern,则返回1,不能则返回0; -nocase的功能同上

  下面的例子是为了区分string match和string equal。string match给的是模版,string equal是要求两个字符串匹配。

1
2
puts [string match "fred*" "freda"];    # 1 
puts [string equal "fred*" "freda"]; # 0

file - 文件

  Tcl脚本中偶尔也会需要用到文件操作,比如新建文件夹,删除文件,复制文件,这些都有对应的命令。

命令 用法 返回值 功能
file normalize file normalize name 字符串 把一个文件名规范化,并且返回这个文件的绝对路径;一般这个name也是包含文件绝对路径的一个字符串
file dirname file dirname name 字符串 返回一个文件的路径,不包含文件名

  file normalize在Vivado的Tcl脚本中也多次用到,这个命令是在Tcl8.4版本才开始支持的。

info - 检视信息

命令 用法 返回值 功能
info exists info exists varName 0/1 如果存在变量varName,返回1;否则返回0
info script info script [filename] 字符串 返回当前执行的脚本文件的名称;如果给定filename,则返回filename,而且后续的info script也会返回filename

source - 执行外部Tcl脚本

  source跟脚本文件名可以执行另一个Tcl文件中的脚本。这么做可以让一个文件不至于太大,实现模块化设计,更好的模块化设计是用packages进行管理。在Vivado中可以将工程创建和Block Design 的创建分为两个Tcl脚本,工程创建之后source 创建Block Design的脚本就行。

namespace - 名称空间

  引入package之后,为了防止不同package之间产生冲突,就有了namespace的概念。::表示全局的名称空间。

后话

  如果你跟我一下只是之后准备用Tcl管理Vivao工程,了解了上面的内容基本就行了。Vivado可以导出Tcl脚本,导出的脚本也很容易看懂,如果有不懂的地方再去查就行。我之后准备用Git和Tcl脚本进行Vivado工程的版本管理。