tcl编程

0. 基础语法

0.1 普通变量

#变量赋值
set x "This is a string"
set y 1.24

#打印变量
puts $x
puts $y

0.2 list, 列表

#列表赋值
set la [list 'a' 'b' {'c' 'd'}]

#llength: 获取list长度
puts [llength $la] ; # 3

#lindex: 根据idx获取元素
set e2 [lindex $la 2]
puts $e2 ; # 'c' 'd'

#lsearch: 在list中查找指定元素
lsearch $la 'b'; # 1, 返回'b'的idx.

#lappend: 追加元素, 可以追加多个
lappend la 'ef' 'gh'; # 注意会修改la本身
puts $la; # 'a' 'b' {'c' 'd'} 'ef' 'gh'

#linsert: 在指定位置插入元素, 可以插入多个
set la_new [linsert $la 1 'x' 'y' 'z']
puts $la_new ; # 'a' 'x' 'y' 'z' 'b' {'c' 'd'}

#lrange: 返回first~last的元素


#lreplace: 替换first~last的元素

#lsort: list排序

#concat: 多个list合并为一个list
set la [list 'a' 'b']
set lb [list 'x' 'y']
set lc [concat la lb]
puts $lc ; # 'a' 'b' 'x' 'y'

0.3 array, 数组

array set arr {}                 ; # 创建空array, 清空已有array

array set arr {a 1 b 2 c 3 de 4} ; # array设置元素, 设置多个
set arr(f) 5                     ; # array设置元素, 设置一个

puts [array size arr]            ; # 5, array长度, key-val对的数目

puts $arr(de)                    ; # 4, 获取key de对应的值, 注意, 不需要在de外面加引号. 

#遍历array
#   de -> 4
#   a -> 1
#   f -> 5
#   b -> 2
#   c -> 3
foreach k [array names arr] {
    set v $arr($k)
    puts "$k -> $v"
}

#判断arr是否存在:
if [array exists arr] {
    puts "array arr exists."
}

#判断key是否存在: info exists arr($k)
#   $arr(b) = 2
if [info exists arr(b)] {
    puts "\$arr(b) = $arr(b)"
}

0.4 循环

0.4.1 for

set la [list 'a' 'b' 'c' 'd' 'e']
for {set i 0} {$i<[llength $la]} {incr i} {
    puts "$i: la[$i]"
}

0.4.2 foreach

#待循环的list
set la [list 'a' 'b' 'c' 'd' 'e']
set lb [list '1' '2' '3' '4' '5' '6']

# 循环单个list, 每次取一个元素
foreach ele $la {
    puts "$ele"
}

# 循环单个list, 每次取多个元素
foreach {ele0 ele1} $la {
    puts "$ele0, $ele1"
}

# 循环多个list, 循环次数以最长的list为准, 不足的补空元素
foreach ea $la eb $lb {
    puts "$ea: $eb"
}

0.5 分支

0.5.1 if..else

set a 100
if {$a == 10} {
    puts "\$a is 10"
} elseif {$a == 20} {
    puts "\$a is 20"
} else {
    puts "\$a is neither 10 nor 20"
}

0.5.2 switch

proc get_instr {location name} {
    set value 0x00
    switch $location {
        "SMSG" {
            switch $name {
                "BYPASS"    {set value 0x00}
                "FLOAD_RUN" {set value 0x30}
                default     {puts "Error: cannot find instr for $location.$name"}
            }
        }
        "PROC" {
            switch $name {
                "BYPASS"   {set value 0x00}
                "BIST_RUN" {set value 0x11}
                default    {puts "Error: cannot find instr for $location.$name"}
            }
        }
        default {puts "Error: first param must be SMSG or PROC"}
    }
    return value
}

puts [get_instr "PROC" "BIST_RUN"]

1. 从命令行获取参数(好像并不是很强大)

array set a0 $argv
foreach opt {-i -o} {
    puts "$opt -> $a0($opt)"
}

运行

$ ex.tcl -i abc -o xyz
-i -> abc
-o -> xyz

2 proc参数

2.1 位置参数

proc add {x y} {       ;# x和y是位置参数
    set r [expr $x+$y] ;# 使用x和y时要加$号, 计算加法时要使用expr
    return $r
}

set a [add 2 4] ;# 调用proc, 如果要赋值给其它变量需要使用set
puts $a         ;# 得到6

2.2 默认参数

注意默认参数要放到参数列表后面, 可以有多个

proc add {x {y 1}} {   ;# y是默认参数, 如果不提供, 则y=1
    set r [expr $x+$y] 
    return $r
}

set a [add 2 4] ;# 调用proc, y提供值
puts $a         ;# 得到6

set a [add 2]   ;# 调用proc, y不提供值, 使用默认值1
puts $a         ;# 得到3

2.3 可变参数

参数数目可变, 使用关键字args接收.

proc add {args} {   ;# args是关键字不能改为其它
    set r 0
    foreach e $args {
        set r [expr $r+$e]
    }
    return $r
}
set a [add 1 2 3]   ;# 调用proc, 提供多个参数
puts $a             ;# 得到6


set a [add 1 2 3 4] ;# 调用proc, 提供多个参数
puts $a             ;# 得到10

2.4 参数解析

在EDA工具中(比如dc_shell, pt_shell等)提供了两个命令: parse_proc_argument和define_proc_atrributes,
可以用来解析proc参数, 但原生的tcl应该不支持.

proc add {args} {
    ;# 解析参数, 并把结果放到变量results中, results是个array
    ;# 注意这个命令需要与define_proc_atrributes联合使用,
    ;# 否则会报告"Error: extra position option '-x_value'"等,
    ;# 意思是这不知道如何解析这些参数.
    parse_proc_arguments -args $args results

    ;# 查看下results的内容
    foreach argname [array names results] {
        puts "$argname: $results($argname)" ;# 打印每个参数和对应值
    }

    ;# 访问参数-x_value, x得到-x_value的值, -x_value是通过define_proc_atrributes定义的.
    set x $results(-x_value) 

    ;# 访问参数-y_value, y得到-y_value的值, 它是可选的, 需要给的默认值
    if [info exists results(-y_value)] {
        set y $results(-y_value) 
    } else {
        set y 0
    }

    ;# 访问参数-z_value, z得到-z_value的值, 它是可选的, 需要给的默认值
    if [info exists results(-z_value)] {
        set z $results(-z_value) 
    } else {
        set z 0
    }

    return [expr $x+$y+$z]
}

;# 给add定义属性
;# 定义的参数(-x_value,-y_value,-z_value), 在交互界面调用命令时, 可以通过tab补全参数全名, 就像dc/pt的命令参数一样
define_proc_attributes add \        
    -info "add at most 3 numbers" \ ;# 帮助信息, 在help add时显示的内容
    -define_args {                  ;# 定义参数
        {-x_value "first  added" x int required} ;# -x_value必须提供
        {-y_value "second added" y int optional} ;# -y_value可选
        {-z_value "third  added" z int optional} ;# -z_value可选
    }

set a [add -x_value 1]                           ;# 1
set a [add -x_value 1 -y_value 2]                ;# 3
set a [add -x_value 1 -y_value 2 -z_value 3]     ;# 6

3. 读写文件

3.1 按行读入文件

set FH [open ../rpt/$file r]
while {[gets $FH line] >= 0} {
    puts $line
}
close $FH

3.2 写文件

set FH [open "file.txt" w]
puts -nonewline $FH "hello"
puts $FH " world."
close $FH

4. 正则匹配

if [regexp {^(\w+)} $line all_match match1] {
    puts $match1
}

5. catch

当运行的命令出现错误时, catch语句返回1

if [catch {open file.txt r} FH] {
    puts "Error: file.txt not opened"
} else {
    puts "open file.txt"
    while {[gets $FH line] >= 0} {
        puts $line
    }
}

6. 非整数运算

set a [expr 100 * double(1)/3] # 返回33.333333333333336%
                               # 如果不使用double, 则只会返回整数部分
                               # [expr 100 * 1/3] 返回33
                               # [expr 1/3 * 100] 返回0
puts "$a%" # 33.333333333333336%

set b [format "%.2f" $a]
puts "$b%" # 33.33% 

7. 四舍五入, 伪随机数

# 四舍五入
set a 2.3
set b [expr round($a)] #注意, 要通过expr调用, 调用时后面要加圆括号
puts $b # 2, 四舍五入2.3变为2

# 生成0~1之间的伪随机小数
set c [expr rand()] # 注意, 要通过expr调用.
puts $c # 0.45033995969519

# 生成0~9之间的伪随机整数
set d [expr round(9*rand())]
puts $d # 5

8. eval和exec

# eval 动态执行tcl语句
set cmd "puts 123"
eval $cmd # 123

# exec 执行unix shell命令(或windows cmd命令)
set a [exec find . -name "run*"]
puts $a # 打印出find命令的标准输出

9. 进制转换

binary用于操作二进制字符串.
包含四个子命令:

binary format: 将普通tcl字符串转换为二进制字符串.
binary scan  : 将二进制字符串转为普通tcl字符串.
binary encode: 将二进制字符串进行编码.
binary decode: 将编码后的二进制字符串进行解码.

二进制字符串的范围是\u0000~\u00FF, 所以如果不想丢失数据, 需要先进行转换.

# 普通tcl字符串, 内容是16进制的字符
set hex_str FF00CC

# 将普通tcl字符串$hex转为二进制格式,
#    H : 表示待处理的字符串中是16进制字符
#    * : 表示待处理的字符串list长度任意??
set bin_fmt [binary format H* $hex_str]

# 将二进制数据($bin_fmt)转为普通tcl字符串, 存储到变量$bits中
binary scan $bin_fmt B* bits

# 打印$bits
puts $bits ;# 1111111110000000011001100


# 将$bits格式化为特定长度的str, %0*s, 中的*, 表示通过变量指定长度
set width 28
set bits_28 [format "%0*s" $width, $bits]
puts $bits_28 ; 00001111111110000000011001100

#遍历$bits_28
set len [string length $bits_28]
for {set i 0} {$i<=[expr $len-1]} {incr i} {
    set i_str [expr $len-1-$i]
    puts "$i -> [string index $bits_28 $i_str]"
}

# 0 -> 0
# 1 -> 0
# 2 -> 1
# 3 -> 1
# 4 -> 0
# 5 -> 0
# 6 -> 1
# 7 -> 1
# 8 -> 0
# 9 -> 0
# 10 -> 0
# 11 -> 0
# 12 -> 0
# 13 -> 0
# 14 -> 0
# 15 -> 0
# 16 -> 1
# 17 -> 1
# 18 -> 1
# 19 -> 1
# 20 -> 1
# 21 -> 1
# 22 -> 1
# 23 -> 1
# 24 -> 0
# 25 -> 0
# 26 -> 0
# 27 -> 0

posted @ 2022-08-02 14:53  编程驴子  阅读(663)  评论(0编辑  收藏  举报