虽然dc也有report_area -hier命令来报告各级模块的面积,本python方案看似有点造轮子,但还是有一定的便利性。一、不受网表类型的限制,综合网表、DFT网表、APR都可以。二、可以过滤面积小于指定值的小模块,比如工具自动插入的ICG模块。三、还可以根据面积占比做排序,方便分析面积的瓶颈。
一、读入网表
下面先读入网表,并分模块识别每个模块内部的stdcell和子模块。这部分与《用python实现分模块按cell类型统计cell个数并降序排列》的方法相同,所以这里直接导入netlistparser.py。
import netlistparser as nlparser
import sys
vlog_netlist_file = sys.argv[2]
modules = nlparser.read_vlog_netlist(vlog_netlist_file)
这样网表就读到了内部python字典里,结构如下:
{
"moduleA": {
"module_name": "moduleA",
"insts": {
"u_AND2_01": "AND2X1",
"u_AND2_02": "AND2X1",
"u_OR2_01": "OR2X1",
"u_INV_01": "INVX1"
}
},
"moduleB": {
"module_name": "moduleB",
"insts": {
"u_AND2_01": "AND2X1",
"u_AND2_02": "AND2X1",
"u_OR2_01": "OR2X1",
"u_INV_01": "INVX1"
}
},
}
二、读入lib库
stdcell的面积信息存储在fab提供的lib文件里,所以我们需要从lib里读到每种cell的面积,方法如下:
# libparser.py
import sys
import re
import json
def read_library(file_name):
cells = {}
lib_lines = open(file_name, 'r').readlines()
cell_start = 0
pin_start = 0
total_lines = len(lib_lines)
print('')
for i in range(total_lines):
line = lib_lines[i]
print('\033[1F {}%'.format(round(100 * i / total_lines)))
cell_s_m = re.search(r'cell\s*\((\w+)\)\s+{', line)
area_m = re.search(r'\sarea\s+:\s+(\S+)\s*;', line)
pin_s_m = re.search(r'\spin\((\w+)\)\s+{', line)
dir_m = re.search(r'\sdirection\s+:\s+(\w+)', line)
func_m = re.search(r'\sfunction\s+:\s+"(.*)"', line)
end_m = re.search(r'}', line)
if cell_s_m:
cell_start = 1
cell = {}
cell_name = cell_s_m.group(1)
cell['cell_name'] = cell_name
pins = []
cell['pins'] = pins
cells[cell_name] = cell
if cell_start and area_m:
area = area_m.group(1)
cell['area'] = round(float(area), 4)
if cell_start and pin_s_m:
pin_start = 1
pin = {}
pin_name = pin_s_m.group(1)
pin['pin_name'] = pin_name
if cell_start and dir_m:
pin_dir = dir_m.group(1)
pin['pin_dir'] = pin_dir
if cell_start and func_m:
pin_func = func_m.group(1)
pin['pin_func'] = pin_func
if cell_start and pin_start and end_m:
pin_start = 0
pins.append(pin)
return cells
def get_cell_area(cells, cell_name):
if cell_name in cells:
return cells[cell_name]['area']
else:
return 0
def is_libcell(cells, cell_name):
if cell_name in cells:
return True
else:
return False
def write_lib_info(lib_info, file_name):
f = open(file_name, 'w')
f.write(json.dumps(lib_info, indent=4))
f.close()
其中,read_library()函数实现了用正则读取lib文件里的cell名字、area、pin、pin方向、function等信息。今天只需要用到cell名字和面积。其它信息是为了后续扩展其它功能做准备。
get_cell_area()提供了读取指定cell面积的接口。is_libcell()实现了判断是libcell还是一般的设计上的子模块。
write_lib_info()可以将lib库的字典写到json文件里,方便调试。
接下来,用这个libparser读入lib库:
import libparser
import sys
lib_file = sys.argv[1]
lib_info = libparser.read_library(lib_file)
三、面积递归统计
我们从top design开始,当遇到stdcell中的cell则累加,当遇到子模块则递归。直到子模块不再含有其它子模块(仅由stdcell组成)时,则返回。
area_info = {}
def report_area(modules, lib_info, module_name):
global area_info
area = 0
insts = modules[module_name]['insts']
for inst in insts:
cell_inst = inst
cell_type = insts[inst]
if cell_type in area_info:
area = area + area_info[cell_type]
elif libparser.is_libcell(lib_info, cell_type):
area = area + libparser.get_cell_area(lib_info, cell_type)
else:
# 递归
report_area(modules, lib_info, cell_type)
area = area + area_info[cell_type]
area_info[module_name] = area
四、打印面积
打印的同时,可以做一些过滤或者排序。
for module in area_info:
if not re.search(r'CLOCK_GATE', module):
print(module, round(area_info[module], 4))
效果如下: