TCL使用
TCL语言入门
1、TCL解释器运用规则把命令分成一个个独立的单词,同时进行必要的置换。
TCL置换分为以下三类。
变量置换 $
命令置换 []
反斜杠置换
而{} 则会将花括号内部的所有字符都解释称普通字符,不会进行置换
# 表示注释
2、变量、数组、列表
变量
定义变量: set 变量名 变量值
使用变量: $变量名
#普通情况下
set a "snow"
puts $a
#若向实现输出 snow_1,怎样操作?
#此时需要利用{}限定$的解释范围
puts ${a}_1
#若利用puts $a_1 则会让$解释a_1,但是$a_1没有定义,所以会报错
数组
数组:TCL中数组可以存储很多值,通过元素名来进行检索。类似于某件事物(数组名)几种不同属性(元素名),每一种属性有其独立的值。
定义数组: set 数组名 (元素名) 值
数组取值:puts $数组名(元素名)
获取数组元素个数: array size 数组名
获取数组元素名: array names 数组名
(System32) 11 % set cell_1(ref_name) "bufferx2"
bufferx2
(System32) 12 % set cell_1(full_name) "top/cell_1"
top/cell_1
(System32) 13 % set cell_1(pins) "A B C"
A B C
(System32) 14 % puts $cell_1(ref_name)
bufferx2
(System32) 20 % array size cell_1
3
(System32) 21 % array names cell_1
ref_name pins full_name
列表
列表式标量的有序集合
定义列表: set 列表名 {元素1 元素2 元素3 ......}
列表取值: $列表名
(System32) 31 % set ivt_list {ivtx1 ivtx2 ivtx3}
ivtx1 ivtx2 ivtx3
(System32) 32 % puts $ivt_list
ivtx1 ivtx2 ivtx3
(System32) 33 %
列表合并,注意,合并之后并未自动生成新列表,若想再次引用,可以利用set 创建新列表,如下的my_list
(System32) 34 % set list1 {bufx1 bufx2 bufx3}
bufx1 bufx2 bufx3
(System32) 35 % set list2 {bufx4 bufx5 bufx6}
bufx4 bufx5 bufx6
(System32) 36 % set list3 {bufx7 bufx8 bufx9}
bufx7 bufx8 bufx9
(System32) 37 % concat $list1 $list2 $list3
bufx1 bufx2 bufx3 bufx4 bufx5 bufx6 bufx7 bufx8 bufx9
#列表合并后利用set生成新列表
(System32) 42 % set my_list [concat $list1 $list2 $list3]
bufx1 bufx2 bufx3 bufx4 bufx5 bufx6 bufx7 bufx8 bufx9
(System32) 43 % puts $my_list
bufx1 bufx2 bufx3 bufx4 bufx5 bufx6 bufx7 bufx8 bufx9
#返回列表中元素个数
(System32) 44 % llength $list1
3
#返回列表中的第2个元素(从0开始计数)
(System32) 45 % set list1 {bufx1 bufx2 bufx3}
bufx1 bufx2 bufx3
(System32) 46 % lindex $list1 2
bufx3
#返回列表中的最后一个元素(一种方法是可以通过查询列表最后一个元素的索引,然后通过lindex找到最后一个元素)
(System32) 51 % lindex $list1 [expr [llength $list1]-1]
f
#在列表尾加入新元素(只能增加一个元素)
(System32) 52 % set a {1 2 3}
1 2 3
(System32) 53 % lappend a 4
1 2 3 4
(System32) 54 % puts $a
1 2 3 4
#直接在数组末尾增加新数组,后一个数组会被视为一个元素,此时类似于二维列表,可以通过先引用到列表b位置,然后再找到4
(System32) 60 % set a {1 2 3}
1 2 3
(System32) 61 % set b {4 5}
4 5
(System32) 62 % lappend a $b
1 2 3 {4 5}
#如何引用4?
(System32) 64 % lindex [lindex [lappend a $b] 3] 0
4
(System32) 65 %
lsort列表指令
使用: lsort 开关 列表
功能: lsort作用在于将列表按照一定规则排序
开关: 默认按照ASCII码进行排序
-real 按照浮点数大小排序
-unique 删除重复元素
(System32) 65 % set list1 {c d a f b}
c d a f b
(System32) 66 % lsort $list1
a b c d f
(System32) 67 % set list2 {-2 3.1 5 0}
-2 3.1 5 0
(System32) 68 % lsort -real $list2
-2 0 3.1 5
(System32) 69 % set list3 {1 1 2 2 3 3}
1 1 2 2 3 3
(System32) 70 % lsort -unique $list3
1 2 3
(System32) 71 %
运算
语法格式: expr 运算表达式
功能: 将运算表达式求值
tips: 如果想要进行浮点运算,需要将其中任意一个数值,写成浮点形式(有小数点),否则会出现5/2=2的情况(即可以用5.0/2=2.5得到正确结果)
数值运算
a+b
a-b
a*b
a/b
(System32) 71 % expr 3+4
7
(System32) 72 %
逻辑运算
a<=b
a>=b
a==b
a!=b
3 控制流
条件指令 if
语法格式: if{判断条件} {
脚本语句
} elseif{判断条件} {
脚本语句
} else {
脚本语句
}
注意,上例中脚本语句的'{'一定要写在上一行,因为如果不这样,TCL解释器会认为命令在换行符处已结束,下一行会被当成新的命令,从而导致错误,同时注意空格的保持
(System32) 7 % if {$a > $b} {
> puts $a
> } else {
> puts $b
> }
3
(System32) 8 %
循环指令foreach
语法格式: foreach 变量 列表 循环主体
语法功能: 从第0个元素开始,每次按顺序取得列表的一个元素,将其赋值给变量,然后执行循环主体一次,直到列表最后一个元素
(System32) 9 % foreach i $list1 {
> puts $i
> }
1
2
3
(System32) 10 %
循环控制指令break
语法格式: break
语法功能: 结束整个循环过程,并从循环跳出
(System32) 10 % set list1 {1 2 3}
1 2 3
(System32) 11 % foreach i $list1 {
> if {$i==2} {
> break
> }
> puts $i
> }
1
(System32) 12 %
循环控制指令continue
语法格式: continue
语法功能: 仅结束本次循环
(System32) 15 % set list1 {3 2 1}
3 2 1
(System32) 16 % foreach i $list1 {
if {$i==2} {
continue
}
puts $i
}
3
1
(System32) 17 %
循环控制指令while
语法格式: while 判断语句 循环主体
语法功能: 如果判断语句成立(返回值非0),就运行脚本,直到不满足判断条件停止循环,此时while命令中断并返回一个空字符串。
(System32) 17 % set i 3
3
(System32) 18 % while {$i >0} {
> puts $i
> incr i -1; #set i [expr $i -1]
> }
3
2
1
(System32) 19 %
#其中上述代码中#后的事注释
循环控制指令for
语法格式: for 初始化参数 判断语句 重新初始化参数 循环主体
语法功能: 如果判断语句返回值非0就进入循环,行循环主体后,再重新初始化参数。然后再次进行判断,直到判断语句返回值为0,循环结束。
(System32) 20 % for {set i 3} {$i>0} {incr i -1} {
> puts $i
> }
3
2
1
(System32) 21 %
4 过程函数
过程函数proc
语法格式: proc 函数名 参数列表 函数主体
语法功能:类似于C语言中的函数。即用户自定义的功能,方便多次调用。
(System32) 23 % proc add {a b} {
> set num [expr $a + $b]
> return $num
> }
(System32) 24 % add 3 4
7
(System32) 25 %
过程函数中的全局变量和局部变量
全局变量:在所有过程之外定义的变量。
局部变量:对于在过程中定义的变量,因为它们只能在过程中被访问,并且当过程退出时会被自动删除。
指令global,可以在过程内部引用全部变量
#在proc内部使用gloabal引用全局变量
(System32) 25 % set a 1
1
(System32) 26 % proc sample {x} {
> global a
> set a [expr $a +1]
> return [expr $a + $x]
> }
(System32) 27 % sample 3
5
(System32) 28 % puts $a
2
(System32) 29 %
#没有在proc内部使用global引用全局变量导致出错
(System32) 31 % set a 1
1
(System32) 32 % proc sample {x} {
set a [expr $a +1]
return [expr $a + $x]
}
(System32) 33 % sample 3
can't read "a": no such variable
(System32) 34 %
5 正则匹配
定义:正则表达式是一种特殊的字符串模式,用来去匹配符合规则的字符串。
\w : 匹配一个字母、数字、下划线
\d : 匹配一个数字
- :匹配0次或者多次
- : 匹配一次或者多次
? :匹配零次或者一次
\s : 匹配空格
. : 匹配任意一个字符
^ : 字符串开头(这是一个锚位,用来指示字符串当中的开头和结尾的位置,使我们能够匹配到正确的字符)
$ : 字符串结尾(这是一个锚位,用来指示字符串当中的开头和结尾的位置,使我们能够匹配到正确的字符)
正则匹配指令regexp
语法格式: regexp ? switches ? exp string ? matchVar? ?subMatchVar subMatchVar ... ?
语法功能: 在字符串中使用正则表达式匹配
- switches:
-nocase将字符串中的大写都当成小写看待。 - exp正则表达式
- string用来进行匹配的字符串
- matchstring表示用正则表示式匹配的所有字符串
- sub1表示正则表达式中的第一个子表达式匹配的字符串
- sub2表示正则表达式中的第二个子表达式匹配的字符串
#匹配字符串"abc456"
(System32) 34 % regexp {\w+\d+} "abc456"
1
#匹配以数字开头且以数字结尾的字符串
(System32) 35 % regexp {^\d.*\w$} "1 dfsal 1"
1
(System32) 36 %
捕获变量()
语法功能:通过()可以捕获字符串
#通过正则匹配得到total结果= 30 years old
#通过()捕获了age结果=30
(System32) 36 % regexp {\s(\d+).*} "Snow is 30 years old" total age
1
(System32) 37 % puts $total
30 years old
(System32) 38 % puts $age
30
#通过()捕获了多个值
(System32) 39 % regexp {^(\w+)\s\w+\s(\d+).*} "Snow is 30 years old" total name age
1
(System32) 40 % puts $total
Snow is 30 years old
(System32) 41 % puts $name
Snow
(System32) 42 % puts $age
30
(System32) 43 %
6 文本处理
常用语法
open
gets
close
- open
语法格式: open 文件 打开方式 (打开方式:r代表读模式,w代表写模式)
语法功能: 打开文件 - gets
语法格式: gets fileid 变量名
语法功能: gets读fileld标识的文件的下一行,并把该行赋给变量,并返回该行的字符数(文件尾返回 -1) - close
语法格式: close fileid
语法功能: 关闭文件
#读取文件
(Desktop) 52 % set INPUTFILE [open file.txt r]
file1a75e298e40
(Desktop) 53 % while {[gets $INPUTFILE line] >=0 } {
> puts "$line"
> }
a
b
c
(Desktop) 55 % close $INPUTFILE
#写入文件并且读取
(Desktop) 60 % set OUTPUTFILE [open file.txt w]
file1a75e283270
(Desktop) 61 % puts $OUTPUTFILE "hello world"
(Desktop) 62 % close $OUTPUTFILE
(Desktop) 63 % set INPUTFILE [open file.txt r]
file1a75e2d62d0
(Desktop) 64 % while {[gets $INPUTFILE line] >=0 } {
> puts "$line"
> }
hello world
(Desktop) 65 % close $INPUTFILE
例子
有文本如下:
#dc.log
Slack=-0.051
Slack=-0.234
Slack=-0.311
Slack=-0.056
Slack=-0.434
利用tcl提取所有Slack之和
tcl脚本如下
set sum 0
set INFILE [open "dc.log" r]
while {[gets $INFILE line] >= 0} {
if {[regexp {^Slack\s+=\s+(-?\d+\.?\d+)} $line match slack]} {
set sum [expr $sum + $slack] ;
}
}
close $INFILE
#测试结果
(Desktop) 40 % while {[gets $INFILE line] >= 0} {
> if {[regexp {^Slack\s+=\s+(-?\d+\.?\d+)} $line match slack]} {
> set sum [expr $sum + $slack] ;
> }
> }
(Desktop) 41 % close $INFILE
(Desktop) 42 % puts $sum
-1.086
(Desktop) 43 %
#正则匹配查找
regexp {([0-9]+)\s([a-z]+)} "there is 100 apples" total num word
# 1
echo $num
# 100
echo $total
# 100 apples
#正则匹配替换
#regsub switchs matching_pattern string replace_string final_string
regsub there "The live there lives" their x
# 1
echo $x
# The live their lives
Synopsys TCL 语言入门
Part-1:TCL 语言
1 DC中常用的TCL指令
- get_ports portsName
功能: 返回design中对应的ports object - get_cells cellsName
功能:返回design中对应的cell的instance name object - note:什么是reference?instance?
例如:
module ADDER();
.....
endmodule
module test();
ADDER u_ADDER();
endmodule
其中ADDER是一个reference,u_ADDER是一个instance
top模块
//**************************************************************************
// *** 名称 : asFIFO.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2020年6月
// *** 描述 : 异步FIFO,读写位宽相同,默认写先读后
//**************************************************************************
module asFIFO
//========================< 参数 >==========================================
#(
parameter DATA_W = 8 , //数据位宽
parameter ADDR_W = 4 //地址位宽
)
//========================< 端口 >==========================================
(
input rst_n , //复位
//FIFO写 ----------------------------------------
input wr_clk , //写时钟
input wr_en , //写使能
input [DATA_W-1:0] wr_data , //写数据
output wr_full , //写满
//FIFO读 ----------------------------------------
input rd_clk , //读时钟
input rd_en , //读使能
output reg [DATA_W-1:0] rd_data , //读数据
output rd_empty //读空
);
//========================< 信号 >==========================================
reg [ADDR_W :0] wr_addr_ptr ; //写指针,多1位
reg [ADDR_W :0] rd_addr_ptr ; //读指针,多1位
wire [ADDR_W :0] wr_addr_gray ; //写地址_格雷码
reg [ADDR_W :0] wr_addr_gray_r ; //写地址打拍
reg [ADDR_W :0] wr_addr_gray_rr ; //写地址打拍
wire [ADDR_W :0] rd_addr_gray ; //读地址_格雷码
reg [ADDR_W :0] rd_addr_gray_r ; //读地址打拍
reg [ADDR_W :0] rd_addr_gray_rr ; //读地址打拍
//-----------------------------------------------
wire [ADDR_W-1:0] wr_addr ; //写地址
wire [ADDR_W-1:0] rd_addr ; //读地址
reg [DATA_W-1:0] ram[2**ADDR_W-1:0] ; //ram,地址位宽4,共16个
//==========================================================================
//== 地址指针
//==========================================================================
always @(posedge wr_clk or negedge rst_n) begin
if(!rst_n) begin
wr_addr_ptr <= 0;
end
else if(wr_en & (!wr_full)) begin
wr_addr_ptr <= wr_addr_ptr + 1;
end
end
always @(posedge rd_clk or negedge rst_n) begin
if(!rst_n) begin
rd_addr_ptr <= 0;
end
else if(rd_en & (!rd_empty)) begin
rd_addr_ptr <= rd_addr_ptr + 1;
end
end
//==========================================================================
//== 空满信号
//==========================================================================
//-- 格雷码转换
//---------------------------------------------------
assign wr_addr_gray = (wr_addr_ptr>>1) ^ wr_addr_ptr;
assign rd_addr_gray = (rd_addr_ptr>>1) ^ rd_addr_ptr;
//-- 跨时钟域,打两拍
//---------------------------------------------------
always @(posedge wr_clk) begin
rd_addr_gray_r <= rd_addr_gray;
rd_addr_gray_rr <= rd_addr_gray_r;
end
always @(posedge rd_clk) begin
wr_addr_gray_r <= wr_addr_gray;
wr_addr_gray_rr <= wr_addr_gray_r;
end
//-- 写满标志:高2位不同,其余位相同
//---------------------------------------------------
assign wr_full = (wr_addr_gray == ({~rd_addr_gray_rr[ADDR_W-:2],rd_addr_gray_rr[ADDR_W-2:0]}));
//-- 读空标志:读写地址相同
//---------------------------------------------------
assign rd_empty = (rd_addr_gray == wr_addr_gray_rr);
//==========================================================================
//== ram读写
//==========================================================================
//-- 读写地址
//---------------------------------------------------
assign wr_addr = wr_addr_ptr[ADDR_W-1:0];
assign rd_addr = rd_addr_ptr[ADDR_W-1:0];
//-- 写数据
//---------------------------------------------------
integer i;
always @(posedge wr_clk or negedge rst_n) begin
if(!rst_n) begin
for(i=0; i<2**ADDR_W; i=i+1)
ram[i] <= 0;
end
else if(wr_en & (!wr_full)) begin
ram[wr_addr] <= wr_data;
end
end
//-- 读数据
//---------------------------------------------------
always @(posedge rd_clk or negedge rst_n) begin
if(!rst_n) begin
rd_data <= 0;
end
else if(rd_en & (!rd_empty)) begin
rd_data <= ram[rd_addr];
end
end
wire flag;
adder u_adder(
.clk(wr_clk),
.rst_n(rst_n),
.flag(flag)
);
endmodule
sub模块
module adder(
input clk,
input rst_n,
output reg flag
);
//************************< 代码 >******************************************
always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
flag <= 1'b0;
end else begin
flag <= 1'b1;
end
end
endmodule
- 原理图如下:
####查找是否有wr_clk的port在design中
design_vision> get_ports wr_clk
{wr_clk}
#####查找是否有AAA的port
design_vision> get_ports AAA
Warning: Can't find port 'AAA' in design 'asFIFO'. (UID-95)
#####查找所有port
design_vision> get_ports *
{wr_data[7] wr_data[6] wr_data[5] wr_data[4] wr_data[3] wr_data[2] wr_data[1] wr_data[0] rd_data[7] rd_data[6] rd_data[5] rd_data[4] rd_data[3] rd_data[2] rd_data[1] rd_data[0] rst_n wr_clk wr_en wr_full rd_clk rd_en rd_empty}
#####查找以wr_开头的port
design_vision> get_ports wr_*
{wr_data[7] wr_data[6] wr_data[5] wr_data[4] wr_data[3] wr_data[2] wr_data[1] wr_data[0] wr_clk wr_en wr_full}
#####查找design中有无u_adder 的cell
design_vision> get_cells u_adder
{u_adder}