library file是IC设计中一种重要的数据文件,用于记录标准单元(及memory)的timing与power信息。
library file遵循liberty format,对于有K库需求的公司而言,K库质量是芯片qualify的重要保证,因此有一些额外的手段对library file进行充分的分析和验证十分必要,这样的工作都依赖于一个健壮的liberty parser。
现在可以公开检索到的liberty parse不多,利用python语言编写的更少,(为什么特别提到python,因为python现在是IC公司自动化流程及inhouse-tool开发的主流语言),因为在实际工作中有这样的需求,因此本文作者自己利用python3开发了一个liberty parser,并将之用到了公司一系列工具之中,效果不错,本着开放互助的原则在此分享给大家。
其github地址为https://github.com/liyanqing1987/libertyParser。
libertyParser下载下来以后包括以下内容。
examples libertyParser.py LICENSE README
其中libertyParser.py包含了核心代码,包括parser和一些实用的function。
README文件中则对liberty parser的基本原理和代码结构做了详细说明。
examples目录中包含一个library文件范例example.lib和一个使用脚本范例example.py,我们就从这个脚本的使用来了解一下libertyParser.py的应用。
example.py
#!/usr/bin/env python3
import os
import re
import sys
os.environ["PYTHONUNBUFFERED"]="1"
cwd = os.getcwd()
upperLevelPath = os.path.dirname(cwd)
sys.path.append(upperLevelPath)
import libertyParser
################
# Main Process #
################
def main():
libFile = './example.lib'
print('')
print('>>> Input file: ' + str(libFile))
myLibertyParser = libertyParser.libertyParser(libFile)
unitDic = myLibertyParser.getUnit()
print('')
print('>>> Unit:')
print(unitDic)
cellList = myLibertyParser.getCellList()
print('')
print('>>> Cell list:')
print(cellList)
cellAreaDic = myLibertyParser.getCellArea()
print('')
print('>>> Cell area:')
print(cellAreaDic)
cellLeakagePowerDic = myLibertyParser.getCellLeakagePower()
print('')
print('>>> Cell leakage_power')
print(cellLeakagePowerDic)
libPinDic = myLibertyParser.getLibPinInfo(cellList=['DFFX1'], pinList=['Q'])
print('')
print('>>> Lib pin info (cell="DFFX1", pin="Q"):')
print(libPinDic)
if __name__ == '__main__':
main()
首先应该将libertyParser.py中的类libertyParser实例化,实例化的时候同时传入library file(s),此时liberty parser会逐行解析library file(s),报告不规范行和非法行,同时将library file(s)的数据记录到python dict(字典)的数据结构之中。(数据结构请参阅README)
myLibertyParser = libertyParser.libertyParser(libFile)
对于myLibertyParser,其使用有两种。
1. 直接获取数据结构myLibertyParser.libDic,自己逐层拨开libDic取出所需数据。
2. 如果对libDic的结构了解不充分,也可以使用libertyParser内置的一些函数来获取基本数据,内置函数有以下几种。
* getUnit() : 返回字典unitDic,里面包含library file中所有关于unit的定义。
* getCellList() : 返回列表cellList,里面包含library file中所有cell。
* getCellArea() : 返回字典cellAreaDic,里面包含library file中所有cell的area信息。
* getCellLeakagePower() :返回字典cellLeakagePowerDic,里面包含library file中所有cell的leakage_power信息,因为一个cell会包含多个leakage_power,所以每个leakage_power的数据结构中会同时包含when/related_pg_pin/values这三个数据以示区分。
* getLibPinInfo() : 返回字典libPinDic,里面包含library file中所有pin层级的timing和internal_power信息,最小单位是timing table和internal_power table中所包含的index及values数据。
那么我们用范例lib(非常简单的一个lib,包含了三个简单cell)来运行一下范例脚本。
[liyanqing@lavaHost1 examples]$ ./example.py
>>> Input file: ./example.lib
>>> Unit:
OrderedDict([('time_unit', '"1ns"'), ('capacitive_load_unit', '(1,pf)'), ('leakage_power_unit', '"1nW"'), ('current_unit', '"1mA"'), ('voltage_unit', '"1V"'), ('pulling_resistance_unit', '"1kohm"')])
>>> Cell list:
['DFFX1', 'INVX1', 'NOR2X1']
>>> Cell area:
OrderedDict([('DFFX1', '0'), ('INVX1', '0'), ('NOR2X1', '0')])
>>> Cell leakage_power
OrderedDict([('DFFX1', [{'value': '5.07033', 'when': '"(CK * D * !(Q) * QN)"'}, {'value': '6.17743', 'when': '"(CK * D * Q * !(QN))"'}, ... ...])])
>>> Lib pin info (cell="DFFX1", pin="Q"):
OrderedDict([('cell', OrderedDict([('DFFX1', OrderedDict([('pin', OrderedDict([('Q', OrderedDict([('timing', [OrderedDict([('related_pin', '"CK"'), ('timing_sense', 'non_unate'), ('timing_type', 'rising_edge'), ('table_type', OrderedDict([('cell_rise', OrderedDict([('template_name', 'delay_template_5x5'), ('index_1', '("0.25, 0.5, 0.75, 1.25, 1.5")'), ('index_2', '("0.015, 0.05, 0.15, 0.3, 0.6")'), ('values', '( "0.517821, 0.652322, 1.02595, 1.58443, 2.70065", "0.549747, 0.684235, 1.05788, 1.61636, 2.73261", ... ... )')]))]))])])]))]))]))]))])
这个parser具有简单高效的特点。
由于采用了基于结构分析(不包含任何关键词),所以它能普遍适用于不同纳米制程的library file(实测过28nm - 7nm)。
尽管python属于一种执行低效的语言,但是经过代码调优,实测parser 700万行的library file(有700MB这么大),用时仅仅28s左右。
欢迎大家使用,如果发现bug请及时联系我liyanqing1987@163.com修复。
如果有其它合理的enhancement的需求,也可以联系我。