1. TCL在EDA工具中的扩展与应用
1.1 design中有哪些对象
Synopsys TCL是基于TCL语言,加了一些命令,可以在DC工具中更加方便地使用。
下面是数字电路design中的对象以及对应的代码示意图,design中主要对象有net、port、cell、pin等等,这些都与代码一一对应。
1.2 综合软件当中TCL的常见指令
语法格式: get_ports portsName
指令功能:返回design中对应的ports object
例-1:如何查看design当中有没有一个port叫做CLK?
Shell> get_ports CLK {CLK}
例-2:我们想查看design当中有没有一个port叫做SPI? Shell> get_ports SPI
No object Found!
例-3:我们想查看design当中所有的port (*可以通配任何字符)
Shell> get_ports *
{A B C D CLK OUT[0] OUT[1]}
例-4:假设我们有port名字叫 {CLKA CLKB OUTA OUTB INA INB}如果我们想得到所有C开头的port 怎么做?
Shell> get_ports C*
{CLKA CLKB}
语法格式: get_cells cellsName
指令功能:返回design中对应的cell的instance name object
这里要区别两个概念:一个是reference name(ref_name),指模块或单元的类型名称,一个是instance name,指对模块或单元在电路中具体实例化时所赋予的名称。
举例-1:我们想查看design当中有没有一个cell叫做U4?
Shell> get_cells U4 {U4}
举例-2:我们想查看design当中所有的cell Shell> get_cells *
{U1 U2 U3 U4}
举例-3:我们想查看design当中以3为结尾的cells
Shell> get_cells *3 {U3}
语法格式: get_nets netsName
指令功能:返回design中net的object
举例-1:查看design当中有没有一个net以INV开头?
Shell> get_nets INV*
{INV0 INV1}
举例-2:我们想查看design当中所有的nets Shell> get_nets *
{A B C D CLK BUS0 BUS1 INV0 INV1 OUT[0] OUT[1]}
举例-3:我们想查看design当中有多少个net? Shell> llength [get_object_name [get_nets *]]
11
Shell> sizeof_collection [get_nets *]
11
语法格式: get_pins pinsName
指令功能:返回design中pin的object
举例-1:查看design当中有哪些pin的名字叫做Z? Shell> get_pins */Z
{INV0/Z INV1/Z}
举例-2:查看design当中有哪些pin的名字以Q开头?
Shell> get_pins */Q*
{ENCODER/Q0 ENCODER/Q1 REGFILE/Q[1] REGFILE/Q[0]}
“数据类型: object (对象) ”与其“属性”
- object是对于tcl脚本一个重要的扩展;
- 常见的对象有四种 cell, net, port, pin;
- 每种object有它的属性。
- 任何一个属性都可以用get_attribute得到,
- list_attribute –class * 可以得到所有object 的属性,
- 部分属性可以用set_attribute来设置。
➢ Cell object
属性 ref_name : 用来保存其map到的reference cell名称
Shell> get_attribute [get_cells –h U3] ref_name
{INV}
➢ Pin object
属性 owner_net : 用来保存与之相连的net的名称
Shell> get_attribute [get_pins U2/A] owner_net {BUS0}
➢ Port object
属性 direction : 用来保存port 的方向
Shell> get_attribute [get_ports A] direction
{in}
Shell> get_attribute [get_ports OUT[1]] direction
{out}
➢ Net object
属性 full_name : 用来保存net的名称
Shell> get_attribute [get_nets INV0] full_name {INV0}
Shell> get_object_name [get_nets INV0] {INV0}
*Shell> get_attribute INV0 full_name Error: No attribute found
理解了属性,就能做更多的事情:
*get_ -f :
-f 这个option可以用来过滤属性,以得到我们想要的object**
例子-1:
想得到所有方向是input的port
Shell> get_ports * –f “direction==in”
{A B C D CLK}例子-2:
想得到所有方向是output的pin
Shell>get_pins * -f “direction ==out”
{U1/Q0 U1/Q1 U2/Z U3/Z REGFILE/Q[0] REGFILE/Q[1]}例子-3:
想得到所有ref_name 是INV的 cell
Shell>get_cells * -f “ref_name == INV” {U2 U3}
*get_ [object]-of:
-of 这个option可以用来得到与你指定object相连接的object**
object的连接关系:
--port object <-> net object >
get_nets –of [get_port A]
A
--net object <-> port object / pin object
> get_net –of [get_pin U2/A]
BUS0
--pin object <-> net object
> get_pin -of [get_net INV1]
U3/Z
--cell object <-> pin object
>get_pins –of [get_cell U4]
{U4/D0 U4/D1 REGFILE/Q1 REGFILE/Q2}
2. 使用TCL语言设计DC的自动化Flow
以一个简单的、入门级的Synopsys DesignComplier自动化方案,供初学者参考。特点如下:
- 该过程无需人为操作,用户只需要输入一条shell启动指令就能完全自动化的完成整个综合过程。
- 具备普适性和可重用性。在综合不同的设计时,只需要修改参数配置文件中的环境变量,不需要修改脚本。
2.1 运行流程
① 运行run.csh脚本,启动DC
#!/bin/csh -f
rm -rf *.log *.svf alib* reports log work #清除之前的文档
mkdir reports work #新建文件夹
dc_shell-xg-t -32bit -f ./top.tcl #启动Design Compiler并运行top.tcl
② 建立reports与work两个文件夹。 reports文件夹用于存放生成的报告, work文件夹用于存放该平台运行过程中生成的文档、脚本。
③ 启动顶层脚本top.tcl文件;
④ top.tcl按先后顺序启动各个子脚本,最终生成script.tcl;
⑤ DC读取script.tcl中的约束,最终完成综合,并将所有报告写入reports文件夹中。
top.tcl脚本运行机制:
- set_library.tcl:生成设定库文件和search path的约束
- read_design.tcl:生成读入设计文件的约束
- create_clock.tcl:生成时钟源相关的约束
- set_rst.tcl:生成复位端口约束的约束
- set_io.tcl:生成输入输出端口的约束
- set_cons.tcl:生成保存门级网表、各种reprot文件的约束
注意:
- 以上所有约束都自动生成,无需人为干预;
- 库文件、代码、时钟、复位、输入输出等均根据代码自动进行匹配,并生成相应的约束;
- 最终所有的约束都被写入script.tcl中,供DC读取,完成最终的逻辑综合过程
2.2 脚本子模块
1. 代码读入约束生成
代码读入约束生成工作流程:
① 设计文件通常使用verilog语言,所以扩展名通常为v,所以先将变量extension设为v;
② 调用filelist.tcl脚本, filelist.tcl脚本会将所有扩展名为v的文件的文件路径输出到一个名为v_list的文档。
③ 打开v_list文档,根据文件的内容将读入设计文件的指令输出到script.tcl脚本。
参考脚本:
set extension v
source [file join $::script_path test/filelist.tcl] #调用filelist.tcl脚本
set des [open [file join $::script_path test/work/v_list] r]
set design [gets $des] #打开v_list文档
for {} {$design!=""} {set design [gets $des]} {
puts $script [format "read_file -format verilog %s" $design]
#输出读入设计文件指令
}
puts $script [format "current_design %s" $top]
#输出设置顶层设计指令
2. 代码filelist生成模块filelist.tcl
本模块用filelist.tcl脚本实现,遍历目标文件下的所有文件,并将扩展名(.v)符合要求的文件完整路径输出到指定的文档,最终形成DC读取verilog代码的约束,写入script.tcl中。
工作流程:
① 将工作路径切换到指定的工作路径
② 判断当前目录下的文件扩展名是否与设置的变量extension一致,如果一致,就将该文件路径输出到指定的文件
③ 如果有文件夹,则递归调用本程序,直至结束。
参考脚本:
proc FindFile { myDir result } {
if {[catch {cd $myDir} err]} {
puts $result $err
return}
foreach myfile [glob -nocomplain *] {
cd $myDir #切换到对应路径
if {[string equal $myfile ""]} {
return } #如果是空文件夹就返回
set fullfile [file join $myDir $myfile]
if {[file isdirectory $myfile]} {
FindFile $fullfile $result #如果有下一级路径则递归调用本函数
} elseif {[string equal [file extension $fullfile] [format ".%s" $::extension]]} {#判断扩展名是否与要求一致
puts $result $fullfile}}}
3. 时钟约束生成子模块
工作流程:
① 首先调用parameter.tcl脚本, 读取其中用户对时钟源指定的参数, 如时钟周期等;
② 调用find_clk.tcl脚本, 该脚本会将搜索顶层设计中的所有的clk端口, 并将所有搜索结果输出到一个名为clk_list的文档;
③ 打开clk_list文档, 将对时钟端口施加约束的指令输出到script.tcl脚本。
参考脚本如下:
source [file join $::script_path test/find_clk.tcl]
#调用find_clk.tcl脚本
set a [open [file join $::script_path test/work/clk_list] r]
#打开v_list文档
set b [gets $a]
set result [open [file join $::script_path test/work/script.tcl] a]
for {} {$b!=""} {set b [gets $a]} {
puts $result [format "create_clock -name "clock" -period %u -waveform {0 %d} { %s }" $::clk_source [expr $::clk_source/2] $b]
#将生成时钟源的指令输出到script.tcl脚本puts $result [format "set_dont_touch_network [get_ports %s]" $b]
#对时钟网络设置don’t touch
puts $result [format "set_drive 0 [get_ports %s]" $b] #设置时钟端口驱动为无穷大
puts $result [format "set_ideal_network [get_ports %s]" $b]
#设置时钟端为理想网线
}close $result
4. 匹配时钟端口子模块find_clk.tcl
功能:
搜索代码中的所有的时钟端口,将结果到work文件夹下的clk_list文档
工作流程:
① 打开v_list文档,在其中找到顶层设计的路径,并打开改设计文件;
② 利用正则表达式匹配其中的clk端口;
③ 并将匹配到的时钟端口的端口名输出到work文件夹下的clk_list文档。
参考代码:
for {} {[eof $designfile]==0} {set fdesign [gets $designfile]} {
if {[regexp {input.*} $fdesign a]} {
#利用正则表达式匹配到声明输入端口那一行
if {[regexp {[^ ,( ]*clk[^ ,;]*} $a rport]} {
#利用正则表达式在那一行匹配后缀为clk的端口puts $fport $rport
#将匹配到的端口名输出到clk_list文档
} }
}
参考资料:
数字集成电路静态时序分析基础_哔哩哔哩_bilibili