TCL脚本是大多数EDA工具的交互命令语言,所以跨平台复用性高。
TCL是一种解释执行的脚本语言,是用一个C/C++语言过程库实现。
运行 tclsh
可在Linux中使用语句tclsh运行TCL文件
tclsh test.tcl
或者使用命令行模式,注意使用exit退出命令行模式
下面对TCL进行介绍
1. 语法
一个TCL脚本可包含一个和多个命令。
每个命令第一个单词表示命令名,其他单词则是字符串类型的参数,相互之间用空格隔开
多个命令之间可用换行符or分号隔开
例如
# 变量x的值是字符串"10"
set x 10;
# 变量y的值是字符串"x+100",而不是110,注意!!!
set y x+100;
TCL注释用#表示
可以将命令名看作是函数名,后面每个空格隔开一个参数。例如set x 10;
就类似C语言中set(x,10);
所以说,参数数目要匹配。例如set a 1 2;
并不会将a的值设定为"1 2",而是报错参数超过了2
那么我想用计算x+100的值并赋给y,怎么操作呢?
1.1. 替换
替换的作用就是告诉TCL解释器,用到的变量的值而非字符串
变量替换 $
含义是将字符串中的"x"替换成 变量x的值"10"
x前加上个$符号就行,即
set y $x+100;
但注意,此处y的值还不是字符串"110",而是字符串"10+100",所以变量替换还没完。
TCL脚本中,除了命令,不带$一律按照字符串处理
我们还需要告诉TCL解释器,将"10+100"当做一个表达式计算
如果有下划线则$会包含下划线,即
set x 1;set x_1 2;set y $x_1;
,则y的值为"2"而不是"1_1"
命令替换 []
含义是将字符串解释成一个命令,例如
# 会报错,认为该命令参数超过2
set y expr $x+100;
# y的值是字符串“110”
set y [expr $x+100];
这里的expr命令就是将后面的字符串看作一个表达式进行计算
反斜杠替换 \
其实就是C语言中的ANSI C中的制表符,这下知道怎么写空格啦
即
#a的值为“1 2 3”
set a 1\ 2\ 3;
TCL支持的反斜杠替换为
反斜线序列 | 含义 |
---|---|
\ | 空格 |
\n | 换行 |
\t | Tab |
双引号 “”
除了直接使用\表示空格外,还可以使用双引号,双引号 只处理$、[]两种替换
例如
set x 10;
set x_1 20;
# 表示y的值为"10 ddd"
set y "$x ddd";
# 想表示为"$x ddd",但报错
set y "$"x" ddd"";
觉得容易搞混的话,可以每次都这么写
set b "abc";set a "10";
就知道那些都是字符串了
2. 变量
对于简单变量,包含两个部分内容,名字和值
set 与 unset
定义一个变量、返回一个变量的值 或 删除多个变量
set与unset的定义如下:
# 为变量定义值
set [变量名] [值];
# 清除变量
unset [变量名1or数组1] [变量名2or数组2] [变量名2or数组2] ...;
例如
set a 1;
set b 2;
set c $a.1;
set e "cdf $ab";
# 删除变量a、b、c、d、e
unset a b c d e;
字符串加 append
把字符串加到某个变量后面
append [变量名] [值];
例子
set a def;
# a的值为"defghi"
append a ghi;
整数加 incr
将原来的值加上一个整数,要求原值和新加的值都是整数
incr [变量名] [整数常量];
例子
set b 2;
incr b 3;
2.2. 字符串
返回字符串 format
返回一个字符串,类似于Systemverilog中的$sformatf()
注意%d、%s等制表符都是通用的
format "[字符串]" $[变量1] $[变量2] $[变量3] ...
例子
set nums 10;
set msg [format "number of pears is %d" $nums];
scan
可以看作是format的逆,format根据几个变量凑成一个字符串,而scan则是从一个字符串中提取信息给几个变量.
#显然带制表符的字符串a 中制表符数目要与后面的变量数目相同
scan "[字符串s]" "[带制表符的字符串s]" [变量1] [变量2] [变量3] ...
例子
scan "number of bananas is 24 6" "number of bananas is %d %d" a b;
puts $a;
puts $b;
执行运算 expr
表示将后面的字符串当做数学函数执行运算,
expr [表达式];
例如
set x 2;
expr $x*sin($x<3);
匹配正则表达式 regexp
判断字符串是否与某正则表达式匹配,返回1或0
正则表达式含义
字符 | 含义 |
---|---|
. | 任意字符检查 |
^ | 从头开始检查 |
$ | 从末尾进行检查 |
a、b、c等字符 | 对应字符检查 |
* | 进行0次或多次检查 |
+ | 进行1次或多次检查 |
3. 数据结构
3.1. 关联数组
是数组但本质上还是关联数组
set 与 unset
set表示向关联数组内添加元素,unset表示去除
set [数组名]([键]) [值];
unset [数组名]([键]);
例子
set day(monday) 1;
set day(tuesday) 2;
set a $day(monday);
unset day(monday);
3.2. 列表
TCL中使用{}来表示一个列表,各元素用空格隔开,列表可以相互嵌套
set a {1 2 3 {4 5 6}};
元素组成列表 list
使用list可返回由多个元素(也可以为列表) 组成的列表
list [元素1] [元素2] [元素3] ......
例子
# l的值为{{a b c d} {e f {g h} i j}},而不是{a b c d e f {g h} i j}!!!
set l [list {a b c d} {e f {g h} i j}];
列表组成列表 concat
使用concat可返回由多个列表 组成的新列表
concat [列表1] [列表2] [列表3] ......
例子
# l的值为{a b c d e f {g h} i j},而不是{{a b c d} {e f {g h} i j}}!!!
set l [concat {a b c d} {e f {g h} i j}];
返回列表元素 lindex
格式如下
lindex [列表] [整数下标];
例子
# a的值为 {e f g}
set a [lindex {a b c d {e f g}} 4];
返回列表元素个数 llength
一维列表元素的个数
llength [列表];
例子
# a的值为 6
set a [llength {a b c d {e f g} h,i,j,k}];
下标插入元素 linsert
返回插入新元素之后的列表
linset [列表] [下标] [元素1] [元素2] [元素3] ...;
例子
# a的值为 {a {1,2,3} {4 5 6} 7 b c d {e f g}}
set a [linsert {a b c d {e f g}} 1 {1,2,3} {4 5 6} 7 ];
列表拆分为TCL脚本,并顺序执行 eval
eval的功能是将列表拆分为TCL脚本,依次执行
# 变量initiate是列表,但写的是脚本
set initiate {
set a 1;
set b 2;
set c 3;
};
set initiate [concat $initiate {set d 4;}];
# 等价于执行set a 1;set b 2;set c 3;set d 4;
eval $initiate;
# 打印 1;set
puts [lindex $initiate 2];
不仅可以拆分命令,还可以拆分值
例如
# 注意这里的filelist的值是一个列表!!!例如{uart_tx.v uart_rx.v uart.v}
set filelist [glob -nocomplain *.v];
# 执行的是 file delete uart_tx.v;file delete uart_rx.v;file delete uart.v;
# 注意不能丢掉eval,否则file delete命令的参数就成了一个由多个文件名组成的列表!!!
eval file delete filelist;
4. 子程序
4.1. 控制流
和众多语言一样,该有的控制语句都有
if
注意写法细节,见下面的原型
# 注意if和{要有一个空格,否则TCL会当做一个命令
# 每个{一定要放在上一行,否则TCL会读完上一行就认为if结束
if {表达式1}{
TCL脚本1;
} elseif {表达式2}{
TCL脚本2;
} elseif {表达式3}{
TCL脚本3;
} else {
TCL脚本4;
}
例子如下
if {$x == 1}{
set nums(first) $x;
}elseif {$x == 2}{
set nums(second) $x;
}elseif {$x == 3}{
set nums(third) $x;
}else {
set nums(first) 0;
incr nums(first);
}
while
写法如下
while {表达式}{
TCL脚本;
}
for
各表达与C语言中的for循环类似
for {表达式1} {表达式2} {表达式3}{
TCL脚本;
}
例如
for {set i 0} {$i <= 10} {incr i 1}{
lappend b [lindex $a $i]
}
foreach
foreach [下标标量] $[列表]{
TCL脚本;
}
switch
类似于case语句,原型如下
switch [可选参数] $[列表]{
[匹配模式1] {TCL脚本1}
[匹配模式2] {TCL脚本2}
...
}
这里的 [可选参数] 如下:
# 精确匹配
-exact
# 与string match匹配方式相同,默认为这个
-glob
# 正则表达式匹配
-regexp
break 与 continue
这个与C语言的含义相同,直接用
4.2. 过程
函数 proc
类似于C语言中的函数,可以定义和调用,也可以使用return
# 过程定义
proc [过程名] { [形参1] [形参2] [形参3] ... }{
TCL脚本1;
return TCL脚本2;
}
# 过程调用
[过程名] [实参1] [实参2] [实参3] ...;
例如
proc adder { nums }{
set sum 0;
for{set i 0;} {$i < 4} {incr i 1}{
incr sum $nums($i);
}
return sum;
}
set a(0) 11;
set a(1) 1;
set a(2) 6;
set a(3) 91;
set res [adder a];
认识全局变量 global
注意TCL的proc内部不认识全局变量,必须使用global a来引用全局变量a
例如
set a 10;
# 只有声明了global a,过程块才认识a
proc use {}{
global a;
return [expr $a+10];
}
5. 文件与目录
5.1. 文件访问
source
读一个文件,并将整个该文件的内容作为一个脚本求值
source e:/test/hmwk.tcl
返回文件句柄 open 与 close
open以某种访问方式打开文件,并返回指向该文件的字符串(类似于指向该文件的句柄)
close加上指向该文件的$字符串,表示关闭
set f [open [文件路径] [访问方式]];
其中[访问方式]有
r:只读,默认方式
r+: 读写
w: 重写,创建/查找文件内容,并清空重写
w+: 读重写,创建/查找文件内容,并清空重写、可读
a: 只写,文件必须存在,且指向文件尾
a+: 读写,文件必须存在,且指向文件尾
# 关闭文件
close $f;
文件读 gets
可多次调用逐行读取文件赋给变量,并返回该行的字符数,文件尾返回-1
# 变量1是可选的,用于获取文件某行的内容
gets $[指向文件的变量] [可选变量1]
例子
proc read { filename }{
set f [open $filename r];
set linesnum 0;
# line是每一行的内容,[gets $f line]返回的是该行的字符数
while { [gets $f line] > 0 }{
incr linesnum 1;
}
return $linesnum;
}
set fn "abc.v";
set fn_lines [read $fn];
文件写 puts
可多次调用将变量的内容逐行写入文件,返回空字符串
同时可以用于打印内容
# 指向文件的变量是可选的,没有就在Linux终端打印
puts $[可选指向文件的变量] [变量]
例子
set name "wdnmd";
puts $name;
5.2. 文件控制
cd、pwd
与Linux一样,用于更改操作目录
注意Linux、TCL表示目录均为’/‘,而不是windows中的’\'
# pwd没有参数,返回当前操作路径
set dir [pwd];
cd [路径字符串]
搜索文件 glob
用于在某目录下使用正则表达式 搜索文件,返回文件名集合
# -nocomplain表示找不到文件不会报错
glob [可选的-nocomplain] [正则表达式];
cd "/home/mywork";
set filelist [glob -nocomplain *.v *.sv];
puts filelist;
文件控制 file
用于对文件进行控制
部分原型如下
# 返回文件是否存在于当前目录下
file exists [文件];
# 创建目录
file mkdir [目录1] [目录2] [目录3] ...;
# 将文件或目录复制到目标目录下, 只有开启-force才会覆盖
file copy [可选的-force] [文件1or目录1] [文件2or目录2] [文件3or目录3] ... [目标目录];
# 删除文件或目录
file delete [文件1or目录1] [文件2or目录2] [文件3or目录3] ... ;
例如对搜索到的某些文件进行删除
# 注意glob返回的是列表,而file delete作用的是多个空格隔开的参数
# 故所以使用eval解析
eval file delete [glob *.v];
# 等价于下面这些
file delete a.v;
file delete b.v;
......